- Single header (https://raw.githubusercontent.com/boost-ext/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/boost-ext/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+ -Wall)
-Wextra -Werror -pedantic -pedantic-errors | /W4 /WX
// 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(typeid(type_list<int>) == typeid(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>>);
template<class... Ts>
constexpr auto stl() {
std::array v{mp::meta<Ts>...};
std::sort(v.begin(), v.end(), [](auto lhs, auto rhs) {
return size_of(lhs) < size_of(rhs);
});
return v;
}
static_assert(std::is_same_v<
std::variant<char[1], char[2], char[3]>,
mp::apply_t<std::variant, stl<char[2], char[1], char[3]>()>
>);
Ranges (https://godbolt.org/z/6dnroa9We)
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 - https://github.com/boost-ext/reflect (https://godbolt.org/z/ds3KMGhqP)
struct foo {
int a;
bool b;
float c;
};
constexpr 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));
Run-time testing/debugging (https://godbolt.org/z/4cP5cPbe4)
template<class... Ts>
constexpr auto reverse() {
std::array v{mp::meta<Ts>...};
std::array<mp::info, sizeof...(Ts)> r;
for (auto i = 0u; i < v.size(); ++i) { r[i] = v[v.size()-i-1]; }
return r;
}
int main() {
static_assert(
std::array{mp::meta<float>, mp::meta<double>, mp::meta<int>}
==
reverse<int, double, float>()
);
assert((
std::array{mp::meta<float>, mp::meta<double>, mp::meta<int>}
==
reverse<int, double, 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, class Expr>
[[nodiscard]] constexpr auto apply(Expr expr);
/**
* Applies vector V to `T<type_of<info>...>`
*
* @code
* static_assert(typeid(variant<int>) ==
* typeid(apply<variant, vector{meta<int>}>));
* @endcode
*/
template<template<class...> class T, auto V>
inline constexpr auto apply_v = decltype(apply<T, [] { return V; }>);
/**
* Applies vector V 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, auto V>
using apply_t = decltype(apply_v<T, V>);
/**
* Invokes function with compile-time info based on run-time info
*
* @code
* info i = meta<conts int>; // run-time
* static_assert(invoke([]<info m> { return std::is_const_v<type_of<m>>; }, i));
* @endcode
*/
constexpr auto invoke(auto fn, info meta);
/**
* Iterates over all elements of constexpr continer
*
* @code
* constexpr vector v{meta<int>};
* for_each<v>([]<info m> {
* static_assert(typeid(int) == typeid(type_of<m>));
* });
* @endcode
*/
template<auto V>
constexpr void for_each(auto fn);
Configuration
#define MP 2'0'2 // Current library version (SemVer)
-
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 won't 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
mp
compares to Reflection for C++26 (https://wg21.link/P2996)?mp
meta-programming model is very simpilar to P2996 and it's 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.P2996 (C++26*) mp (C++20) ^T
meta<T>
typename [: T :]
type_of<T>
template for
(https://wg21.link/p1306)for_each
substitute
apply
value_of<R>(reflect_invoke(^fn, {substitute(^meta, {reflect_value(m)})}))
invoke(fn, m)
-
How to integrate with CMake/CPM?
CPMAddPackage( Name mp GITHUB_REPOSITORY boost-ext/mp GIT_TAG v2.0.2 ) add_library(mp INTERFACE) target_include_directories(mp SYSTEM INTERFACE ${mp_SOURCE_DIR}) add_library(mp::mp ALIAS mp)
target_link_libraries(${PROJECT_NAME} mp::mp);
-
Similar projects?