Last active
December 6, 2020 03:51
-
-
Save KaiserKatze/9556c7d62db379714b8ccceb3137e91b to your computer and use it in GitHub Desktop.
C++ Meta Programming Examples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ///////////////////// 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