3 ms·
C++20 – Back to the Future Meta-Programming
- kris-jusiak 2y ago# Meta-Programming is one of the C++ super-powers Traditional meta-programming model in C++ is very powerful but not the easiest to grasp. Mainly because it has different syntax than 'normal' C++ with a lot of angle brackets, it's functional, immutable, etc. There are some great template meta-programming libraries available such as boost.mp11 - https://www.boost.org/doc/libs/1_85_0/libs/mp11/doc/html/mp11.html https://www.boost.org/doc/libs/1_85_0/libs/mp11/doc/html/mp1... which make template meta-programming much simpler. The question is - Can we do better? And if so, what are the trade-offs? All in all, wouldn't be great to be able to write the same code for run-time and compile-time and/or debug compile-time code at run-time? Reflection for C++ - https://wg21.link/P2996 https://wg21.link/P2996 - introduced a new meta-programming model which is value/consteval based and can greatly improve the experience. Together with reflection is a very powerful combination but it also has its own set of trade-offs such as slower compilation-times. This post is about `mp` - https://github.com/boost-ext/mp https://github.com/boost-ext/mp - meta-programming library which supports - similar to P2996 - meta-programming model for easier transition as it supports C++20 (msvc, gcc, clang), has a bit faster compilation times than P2996, but mostly, it makes meta-programming a 'normal' C++. In the mp world no difference between run-time and compile-time, whole standard library can be leveraged and it has reflection integration with C++20 using https://github.com/boost-ext/reflect https://github.com/boost-ext/reflect. Of course it has it own set of trade-offs but, IMHO, it has a lot of potential and it's super fan. > Example (API) // mp::meta static_assert(mp::meta<int> == mp::meta<int>); static_assert(mp::meta<int> != mp::meta<void>); static_assert(typeid(mp::meta<int>) == typeid(mp::meta<void>)); // mp::type_of constexpr mp::info meta = mp::meta<int>; mp::type_of<meta> i{}; // same as int i{}; mp::type_of<mp::meta<bool>> b = true; // same as bool b = true; // mp::apply template<class...> struct type_list{ }; static_assert(std::is_same_v<type_list<int, int>, mp::apply_t<type_list, mp::array{meta, meta}>>); // mp::invoke static_assert(not mp::invoke<std::is_const>(meta)); static_assert(std::is_same_v<const int, mp::type_of<mp::invoke<std::add_const>(meta)>>); // hello world template<size_t N, class... Ts> using at_c = mp::type_of<std::array{mp::meta<Ts>...}[N]>; static_assert(std::is_same_v<int, at_c<0, int, bool, float>>); static_assert(std::is_same_v<bool, at_c<1, int, bool, float>>); static_assert(std::is_same_v<float, at_c<2, int, bool, float>>); // ranges template<class... Ts> constexpr mp::vector ranges = std::array{mp::meta<Ts>...} | std::views::drop(1) | std::views::reverse | std::views::filter([](auto m) { return mp::invoke<std::is_integral>(m); }) | std::views::transform([](auto m) { return mp::invoke<std::add_const>(m); }) | std::views::take(2) ; static_assert(std::is_same_v< std::variant<const int, const short>, mp::apply_t<std::variant, ranges<double, void, const short, int>> >); // reflection (requires https://github.com/boost-ext/reflect) struct foo { int a; bool b; float c; }; foo f{.a = 42, .b = true, .c = 3.2f}; constexpr mp::vector v = reflexpr(f) | std::views::filter([&](auto meta) { return member_name(meta, f) != "b"; }) ; static_assert(std::tuple{42, 3.2f} == unreflexpr<std::tuple, v>(f)); > Full example - standalone - https://godbolt.org/z/Mjcxedzzj https://godbolt.org/z/Mjcxedzzj > Full example - reflection - https://godbolt.org/z/ds3KMGhqP https://godbolt.org/z/ds3KMGhqP > Library - https://github.com/boost-ext/mp https://github.com/boost-ext/mp > Supported compilers - https://godbolt.org/z/qarWdbK79 https://godbolt.org/z/qarWdbK79 > Compilation times benchmark - https://boost-ext.github.io/mp https://boost-ext.github.io/mp
- stefanos82 2y agoThis is quite impressive, kudos! What I liked about it is the table comparison in repo's FAQ between C++26's Reflection and this library and I have to admit, I prefer this library from Reflection's implementation; I find it more readable and understandable.