// Overview / Examples / API / FAQ
- Single header (https://raw.githubusercontent.com/qlibs/mp/main/mp - for integration see FAQ)
- 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>})
));
}
/**
* 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) {
/**
* 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>
constexpr auto for_each(auto&& fn);
/**
* Asserts that condition is satisfied
* - constant evaluation: calls undefined function `failed()`
* - run-time evaluation: #ifndef NDEBUG calls `breakpoint()` otherwise empty call
*/
constexpr void expect(auto cond) requires requires { bool(cond); };
-
What does it mean that
mp
tests itself upon include?mp
runs 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
-DNTEST
is 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?
Slides - Template-less Metaprogramming
Source code - https://godbolt.org/z/Kf9rovaqE
- ~100 LOC, C++17, gcc, clang, msvc, no dependencies
-
How
mp
compares to Reflection for C++26 (https://wg21.link/P2996)?mp
meta-programming model is very simpilar to P2996 and its based on type erased info object and meta-functions.mp
also supports all C++ standard library and since verion 2.0.0+mp
type names have been adopted to closer reflect the reflection proposal.mp (C++20) P2996 (C++26*) meta<T>
^T
using info = decltype(meta<void>)
using info = decltype(^::)
type_of<T>
typename [: T :]
for_each
template for
(https://wg21.link/p1306)apply_t
substitute
invoke<Fn>
reflect_invoke
invoke<Trait>
test_trait
invoke<T>
extract
-
How to integrate with CMake.FetchContent?
include(FetchContent) FetchContent_Declare( qlibs.mp GIT_REPOSITORY https://github.com/qlibs/mp GIT_TAG v2.0.10 ) FetchContent_MakeAvailable(qlibs.mp)
target_link_libraries(${PROJECT_NAME} PUBLIC qlibs.mp);
-
Similar projects?