Skip to content

Instantly share code, notes, and snippets.

@KaiserKatze
Last active December 6, 2020 03:51
Show Gist options
  • Select an option

  • Save KaiserKatze/9556c7d62db379714b8ccceb3137e91b to your computer and use it in GitHub Desktop.

Select an option

Save KaiserKatze/9556c7d62db379714b8ccceb3137e91b to your computer and use it in GitHub Desktop.
C++ Meta Programming Examples
#include <iostream>
#include <type_traits>
#include <atomic>
// Truncate larger integers into smaller ones
template <typename TargetType, typename Integer,
std::enable_if_t<std::conjunction_v<std::is_integral<Integer>, std::is_integral<TargetType>>, int> = 0>
constexpr inline TargetType AutoTrunc(Integer value)
{
if constexpr (sizeof(TargetType) == sizeof(char))
return static_cast<TargetType>(0xff & value);
if constexpr (sizeof(TargetType) == sizeof(short))
return static_cast<TargetType>(0xffff & value);
if constexpr (sizeof(TargetType) == sizeof(int))
return static_cast<TargetType>(0xffffffff & value);
return static_cast<TargetType>(value);
}
template <typename TargetType, typename Integer,
std::enable_if_t<std::conjunction_v<std::is_integral<Integer>, std::is_integral<TargetType>>, int> = 0>
constexpr inline std::atomic<TargetType> AutoTrunc(const std::atomic<Integer>& value)
{
if constexpr (sizeof(TargetType) == sizeof(char))
return static_cast<TargetType>(0xff & value.load());
if constexpr (sizeof(TargetType) == sizeof(short))
return static_cast<TargetType>(0xffff & value.load());
if constexpr (sizeof(TargetType) == sizeof(int))
return static_cast<TargetType>(0xffffffff & value.load());
return static_cast<TargetType>(value.load());
}
int main()
{
constexpr int x = AutoTrunc<int>(5000000000); // '5000000000' here should be a 'long long' integer
std::cout << "x = " << x << std::endl;
// expected output:
// x = 705032704
return 0;
}
// Construct a binary constant in human-readable form
// NOTE: C++14 supports the binary integer literal
// @see: https://en.cppreference.com/w/cpp/language/integer_literal
template <typename Integer, Integer N>
struct binary
{
static_assert(N >= 0, "Invalid template argument: N should be either positive or zero!");
// Keyword `constexpr` is supported in C++11, replace it with `const` if you don't have C++11
constexpr static Integer value = binary<Integer, N / 10>::value << 1 | N % 10;
};
template <> struct binary<char, char{ 0 }> { constexpr static char value{ 0 }; };
template <> struct binary<short, short{ 0 }> { constexpr static short value{ 0 }; };
template <> struct binary<int, int{ 0 }> { constexpr static int value{ 0 }; };
template <> struct binary<long, long{ 0 }> { constexpr static long value{ 0 }; };
template <> struct binary<long long, long long{ 0 }> { constexpr static long long value{ 0 }; };
template <> struct binary<unsigned char, unsigned char{ 0 }> { constexpr static unsigned char value{ 0 }; };
template <> struct binary<unsigned short, unsigned short{ 0 }> { constexpr static unsigned short value{ 0 }; };
template <> struct binary<unsigned int, unsigned int{ 0 }> { constexpr static unsigned int value{ 0 }; };
template <> struct binary<unsigned long, unsigned long{ 0 }> { constexpr static unsigned long value{ 0 }; };
template <> struct binary<unsigned long long, long long{ 0 }> { constexpr static unsigned long long value{ 0 }; };
#include <iostream>
int main()
{
auto b{ binary<int, 1010>::value };
std::cout << "Value = " << b
<< " "
<< std::boolalpha
<< (b == 0b1010) // 0b1010, a binary integer literal, only compiles in C++14
<< std::noboolalpha
<< std::endl;
return 0;
}
#include <iostream>
template <int Src, int Dst, int Unit,
typename Function>
constexpr void ForLoop(Function function)
{
static_assert(Unit > 0 && Src <= Dst
|| Unit < 0 && Src >= Dst,
"Invalid template argument: Possible infinite loop!");
if constexpr (Unit > 0 && Src < Dst
|| Unit < 0 && Src > Dst)
{
// TODO: put your code here, do something
function(Src, Dst, Unit);
ForLoop<Src + Unit, Dst, Unit, Function>(function);
}
}
void Print(int Src, int Dst, int Unit)
{
std::cout << "Loop @ <" << Src << ", " << Dst << ", " << Unit << ">" << std::endl;
}
int main()
{
ForLoop<0, 10, 1>(Print);
return 0;
}
// 2-level loops
#include <iostream>
#include <type_traits>
template <int _Src, int _Dst, int _Inc, int _Pos = _Src>
struct ForLoop_Range_t
{
static_assert(_Inc > 0 && _Src <= _Dst && _Pos <= _Dst
|| _Inc < 0 && _Src >= _Dst && _Pos >= _Dst,
"Invalid template argument: Possible infinite loop!");
using Next = ForLoop_Range_t<_Src, _Dst, _Inc, _Pos + _Inc>;
using Reset = ForLoop_Range_t<_Src, _Dst, _Inc, _Src>;
constexpr static bool IsValid{ _Inc > 0 && _Pos < _Dst || _Inc < 0 && _Pos > _Dst };
constexpr static int Src{ _Src };
constexpr static int Dst{ _Dst };
constexpr static int Pos{ _Pos };
};
struct ForLoop_t
{
// Without Context
template <typename XRange, typename YRange, typename Function>
constexpr static void pass1()
{
if constexpr (XRange::IsValid)
{
pass2<XRange, YRange, Function>();
}
}
template <typename XRange, typename YRange, typename Function>
constexpr static void pass2()
{
if constexpr (YRange::IsValid)
{
// the innermost loop
Function::template run<XRange, YRange>();
pass2<XRange, typename YRange::Next, Function>();
}
else if constexpr (!YRange::IsValid)
{
// the nesting loop is over;
// restart the outter loop
pass1<typename XRange::Next, typename YRange::Reset, Function>();
}
}
// With Context
template <typename XRange, typename YRange, typename Function, typename Context>
constexpr static void pass1(Context&& context)
{
if constexpr (XRange::IsValid)
{
pass2<XRange, YRange, Function>(context);
}
}
template <typename XRange, typename YRange, typename Function, typename Context>
constexpr static void pass2(Context&& context)
{
if constexpr (YRange::IsValid)
{
// the innermost loop
Function::template run<XRange, YRange>(context);
pass2<XRange, typename YRange::Next, Function>(context);
}
else if constexpr (!YRange::IsValid)
{
// the nesting loop is over;
// restart the outter loop
pass1<typename XRange::Next, typename YRange::Reset, Function>(context);
}
}
};
struct Function_t_1
{
template <typename XRange, typename YRange>
constexpr static void run()
{
}
};
struct Function_t_2
{
template <typename XRange, typename YRange>
constexpr static void run(int& num)
{
}
};
int main()
{
ForLoop_t::pass1<
ForLoop_Range_t<0, 4, 1>,
ForLoop_Range_t<0, 4, 1>,
Function_t_1
>();
ForLoop_t::pass1<
ForLoop_Range_t<0, 4, 1>,
ForLoop_Range_t<0, 4, 1>,
Function_t_2
>(50);
return 0;
}
#include <iostream>
namespace detail
{
template <typename _ToType, typename _FromType>
constexpr inline _ToType int_cast(_FromType value)
{
static_assert(std::is_void_v<_FromType>,
"_FromType should be integral!");
}
template <>
constexpr inline unsigned int int_cast<unsigned int, unsigned long long>(unsigned long long value)
{
return 0xffffffff & value;
}
template <>
constexpr inline unsigned int int_cast<unsigned int, unsigned int>(unsigned int value)
{
return value;
}
template <>
constexpr inline unsigned int int_cast<unsigned int, unsigned short>(unsigned short value)
{
return value;
}
template <>
constexpr inline unsigned int int_cast<unsigned int, unsigned char>(unsigned char value)
{
return value;
}
template <>
constexpr inline unsigned short int_cast<unsigned short, unsigned long long>(unsigned long long value)
{
return 0xffff & value;
}
template <>
constexpr inline unsigned short int_cast<unsigned short, unsigned int>(unsigned int value)
{
return 0xffff & value;
}
template <>
constexpr inline unsigned short int_cast<unsigned short, unsigned short>(unsigned short value)
{
return value;
}
template <>
constexpr inline unsigned short int_cast<unsigned short, unsigned char>(unsigned char value)
{
return value;
}
template <>
constexpr inline unsigned char int_cast<unsigned char, unsigned long long>(unsigned long long value)
{
return 0xff & value;
}
template <>
constexpr inline unsigned char int_cast<unsigned char, unsigned int>(unsigned int value)
{
return 0xff & value;
}
template <>
constexpr inline unsigned char int_cast<unsigned char, unsigned short>(unsigned short value)
{
return 0xff & value;
}
template <>
constexpr inline unsigned char int_cast<unsigned char, unsigned char>(unsigned char value)
{
return value;
}
}
int main()
{
constexpr unsigned int x = detail::int_cast<unsigned int>(5000000000ULL); // '5000000000' here should be a 'long long' integer
std::cout << "x = " << x << std::endl;
// expected output:
// x = 705032704
std::cout
<< "a = " << int{ (~0) & 0x7fffffff } << '\n'
<< "b = " << static_cast<unsigned int>((~0)) << '\n'
<< "b = " << static_cast<unsigned int>((~0) & 0x7fffffff) << '\n'
<< std::hex
<< "a = " << int{ (~0) & 0x7fffffff } << '\n'
<< "b = " << static_cast<unsigned int>((~0) & 0x7fffffff) << '\n'
;
return 0;
}
#include <iostream>
namespace MetaMath
{
template <int LHS, int RHS>
constexpr static bool LargerThan = LHS > RHS;
template <int LHS, int RHS>
constexpr static bool Equal = LHS == RHS;
template <int Number>
constexpr static bool IsOdd = (Number & 1) == 1;
template <int Number>
constexpr static bool IsEven = (Number & 1) == 0;
template <int Base, int N>
constexpr static int power = Base * power<Base, N - 1>;
template <int Base>
constexpr static int power<Base, 1> = Base;
template <int Base>
constexpr static int power<Base, 0> = 1;
constexpr int ceil(double value)
{
int ires{ static_cast<int>(value) };
return (value == static_cast<double>(ires))
? ires : (ires + (value > 0 ? 1 : 0));
}
constexpr int log2(int value)
{
if (value <= 0)
return -1; // ERROR!
value >>= 1;
int count{ 0 };
while (value != 0)
{
++count;
value >>= 1;
}
return count;
}
constexpr int log10(int value)
{
if (value <= 0)
return -1; // ERROR!
double result{ log2(value) / 3.32192809488736 };
return ceil(result);
}
}
using namespace MetaMath;
int main()
{
std::cout // Expected output:
<< "10 ** 4 = " << power<10, 4> << std::endl // 10 ** 4 = 10000
<< "3 ** 5 = " << power<3, 5> << std::endl // 3 ** 5 = 243
<< "log10(10) = " << log10(10) << std::endl
<< "log10(100) = " << log10(100) << std::endl
<< std::endl;
return 0;
}
// Solution 1: Template Variable
// Template variable is introduced in C++14
// This solution is very simple but not robust enough
// because there is no checking about
// whether template argument 'N' is negative integer
// which could lead to compile-time error
// and waste of time when compiling
template <int N> constexpr static int FactorialVariable = N * FactorialVariable<N - 1>;
template <> constexpr static int FactorialVariable<0> = 1;
// Solution 2: Template struct with constexpr value
// This solution involves no template variable
// so it works with C++11 (constexpr is introducted in C++11)
// Although this solution is quite simple and easy to implement
// there is no caching of the used factorial values
template <int N>
struct SimpleFactorial
{
static_assert(N >= 0, "Invalid template argument: N < 0!");
constexpr static int value{ N * SimpleFactorial<N - 1>::value };
};
template <> struct SimpleFactorial<0> { constexpr static int value{ 1 }; };
// Solution 3: Yet another template struct
// This solution implements caching the used factorial values
#include <array> // necessary STL header for struct Factorial
template <int N, typename Integer = int>
struct Factorial
: public std::array<Integer, N>
{
static_assert(N >= 0, "Invalid template argument: N < 0!");
using base_type = std::array<Integer, N>;
constexpr Factorial()
{
this->base_type::at(0) = 1;
this->base_type::at(1) = 1;
for (Integer i = 2; i < N; i++)
{
this->base_type::at(i) = i * this->base_type::at(i - 1);
}
}
};
// Test suite of solution-3
#include <iostream>
constexpr static Factorial<10> cache;
struct FactorialAssert
{
constexpr static bool validation = cache[0] == 1
&& cache[1] == 1
&& cache[2] == 2
&& cache[3] == 6
&& cache[4] == 24
&& cache[5] == 120
&& cache[6] == 720
&& cache[7] == 5040;
};
int main()
static_assert(FactorialAssert::validation, "ERROR!");
for (auto i = cache.size() - 1; i > 0; i--)
{
std::cout << i << "! = " << cache[i] << std::endl;
}
return 0;
}
#include <iostream>
template <typename CharType, int Length>
void takeString(CharType const(&input)[Length])
{
std::cout << "String \""
<< input
<< "\" has "
<< Length - 1 // BEWARE! C-style string ends with '\0'
<< " length!"
<< std::endl;
}
int main()
{
takeString("Hello world!");
// Expected output:
// String "Hello world!" has 12 length!
return 0;
}
///////////////////// Factorial /////////////////////
template <int N>
struct Factorial
{
static_assert(N >= 0, "Negative argument `N`!");
enum { val = Factorial<N - 1>::val * N };
};
template<> struct Factorial<0> { enum { val = 1 }; };
///////////////////// Permutation /////////////////////
template <int K, int N>
struct Permutation
{
static_assert(K >= 0, "Negative argument `K`!");
static_assert(K <= N, "Invalid arguments `K > N`!");
enum { val = Factorial<N>::val / Factorial<N - K>::val };
};
///////////////////// Combination /////////////////////
template <int K, int N>
struct Combination
{
static_assert(K >= 0, "Negative argument `K`!");
static_assert(K <= N, "Invalid arguments `K > N`!");
enum { val = Permutation<K, N>::val / Factorial<K>::val };
};
///////////////////// Fibonacci /////////////////////
template <int N>
struct Fibonacci
{
static_assert(N >= 0, "Negative argument `N`!");
enum { val = Fibonacci<N - 1>::val + Fibonacci<N - 2>::val };
};
template<> struct Fibonacci<0> { enum { val = 0 }; };
template<> struct Fibonacci<1> { enum { val = 1 }; };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment