// Overview / Examples / API / FAQ / Resources
- Single header (https://raw.githubusercontent.com/qlibs/mp/main/mp) / C++20 module (https://raw.githubusercontent.com/qlibs/mp/main/mp.cppm)
- Minimal API and learning curve (supports STL, ranges, ...)
- Supports debugging (meta-functions can be executed and debugged at run-time - see examples)
- Supports reflection (requires https://github.com/qlibs/reflect - see examples)
- Verifies itself upon include (can be disabled with
-DNTEST- see FAQ) - Optimized compilation-times (see benchmarks)
- C++20 (clang++13+, g++11+, msvc-19.34+)
// 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>, mp::apply_t<type_list, std::array{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)>>); int main() { // mp::for_each constexpr auto v = mp::vector{meta}; mp::for_each<v>([&]<mp::info meta>{ /* ... */ }); } // and more (see API)...Hello World (https://godbolt.org/z/69jGzqPs1)
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>>);Algorithms (https://godbolt.org/z/GvzjvdPq8)
template<class... Ts> struct example { mp::apply_t<std::variant, 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) | std::ranges::to<mp::vector<mp::info>>() > v; }; static_assert( typeid(std::variant<const int, const short>) == typeid(example<double, void, const short, int>::v) );Reflection - https://github.com/qlibs/reflect (https://godbolt.org/z/qb37G79Ya)
struct foo { int a; bool b; float c; }; constexpr foo f{.a = 42, .b = true, .c = 3.2f}; constexpr mp::vector<mp::info> v = members(f) | std::views::filter([&](auto meta) { return member_name(meta, f) != "b"; }) ; static_assert(std::tuple{42, 3.2f} == to<std::tuple, v>(f));Run-time testing/debugging (https://godbolt.org/z/vTfGGToa4)
constexpr auto reverse(std::ranges::range auto v) { std::reverse(v.begin(), v.end()); return v; } int main() { static_assert( std::array{mp::meta<float>, mp::meta<double>, mp::meta<int>} == reverse(std::array{mp::meta<int>, mp::meta<double>, mp::meta<float>}) ); assert(( std::array{mp::meta<float>, mp::meta<double>, mp::meta<int>} == reverse(std::array{mp::meta<int>, mp::meta<double>, mp::meta<float>}) )); }namespace mp::inline v2_0_11 { /** * Meta info type */ enum class info : size_t { }; /** * Creates meta type * * @code * static_assert(meta<void> == meta<void>); * static_assert(meta<void> != meta<int>); * @endcode */ template<class T> inline constexpr info meta = /* unspecified */; /** * Returns underlying type from meta type * * @code * static_assert(typeid(type_of<meta<void>>) == typeid(void)); * @endcode */ template<info meta> using type_of = /* unspecified */; /** * Applies invocable `[] { return vector<info>{...}; }` to * `T<type_of<info>...>` * * @code * static_assert(typeid(variant<int>) == * typeid(apply<variant>([] { return vector{meta<int>}; }))); * @endcode */ template<template<class...> class T> [[nodiscard]] constexpr auto apply(concepts::invocable auto expr); /** * Applies range to `T<type_of<info>...>` * * @code * static_assert(typeid(variant<int>) == * typeid(apply<variant, vector{meta<int>}>)); * @endcode */ template<template<class...> class T, concepts::range auto range> inline constexpr auto apply_v = decltype(apply<T, [] { return range; }>); /** * Applies range to `T<type_of<info>...>` * * @code * static_assert(typeid(variant<int>) == * typeid(apply_t<variant, [] { return vector{meta<int>}; }>)); * @endcode */ template<template<class...> class T, concepts::range auto range> using apply_t = decltype(apply_v<T, range>); /** * Invokes function with compile-time info based on meta-info * * @code * static_assert(invoke( * []<info m> { return std::is_const_v<type_of<m>>; * }, meta<conts int>)); * @endcode */ [[nodiscard]] constexpr auto invoke(auto&& fn, info meta); /** * Invokes type_trait with meta-info * * @code * static_assert(not invoke<std::is_const>(meta<int>)); * static_assert(invoke<std::is_const>(meta<const int>)); * @endcode */ template<template<class...> class T, class... Ts> [[nodiscard]] constexpr auto invoke(info meta); /** * unrolls fn N times # optionally passes index I to fn * @code * mp::unroll<3>([]{ std::print("mp"); }); // prints 'mpmpmp' * mp::unroll<3>([]<auto I>{ std::print("{}", I); }); // prints '012' * @endcode */ template<size_t N> inline constexpr auto unroll(auto&& fn); /** * Iterates over all elements of a range * * @code * constexpr vector v{meta<int>}; * for_each<v>([]<info m> { * static_assert(typeid(int) == typeid(type_of<m>)); * }); * @endcode */ template<concepts::range auto range> inline constexpr auto for_each(auto&& fn); } // namesapce mp
What does it mean that
mptests itself upon include?
mpruns all tests (via static_asserts) upon include. If the include compiled it means all tests are passing and the library works correctly on given compiler, enviornment.How to disable running tests at compile-time?
When
-DNTESTis defined static_asserts tests wont be executed upon include. Note: Use with caution as disabling tests means that there are no gurantees upon include that given compiler/env combination works as expected.How does it work?
Template-less Metaprogramming
- Video - https://www.youtube.com/watch?v=yriNqhv-oM0
- Slides - https://kris-jusiak.github.io/talks/cppcon-2024
- Source code - https://godbolt.org/z/Kf9rovaqE (~100 LOC, C++17, gcc, clang, msvc, no dependencies)
How
mpcompares to Reflection for C++26 (https://wg21.link/P2996)?
mpmeta-programming model is very simpilar to P2996 and its based on type erased info object and meta-functions.mpalso supports all C++ standard library and since verion 2.0.0+mptype names have been adopted to closer reflect the reflection proposal.
mp (C++20) P2996 (C++26*) meta<T>^^Tusing info = decltype(meta<void>)using info = decltype(^^::)type_of<T>typename [: T :]for_eachtemplate for(https://wg21.link/p1306)apply_tsubstituteinvoke<Fn>reflect_invokeinvoke<Trait>test_traitinvoke<T>extractSimilar projects?
- https://wg21.link/P2996 (Reflection for C++26)
- https://github.com/seanbaxter/circle (Circle-lang)
- https://zig.guide/language-basics/comptime (Zig-comptime)