Skip to content
/ mp Public

C++20 Meta-Programming library

Notifications You must be signed in to change notification settings

qlibs/mp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 

Repository files navigation

// Overview / Examples / API / FAQ / Resources

MP: Template Meta-Programming library

MIT Licence Version Build Try it online

https://en.wikipedia.org/wiki/Template_metaprogramming

Features

Requirements

Overview

API (https://godbolt.org/z/zTdYGvKKW)

// 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)...

Examples

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>})
  ));
}

API

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

FAQ

  • 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?

    Template-less Metaprogramming

  • 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
  • Similar projects?

    boost.mp11, boost.hana, boost.fusion, boost.mpl

Resources

License