Hacker News new | ask | show | jobs
by kris-jusiak 706 days 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/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 - 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 - 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. 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

> Full example - reflection - https://godbolt.org/z/ds3KMGhqP

> Library - https://github.com/boost-ext/mp

> Supported compilers - https://godbolt.org/z/qarWdbK79

> Compilation times benchmark - https://boost-ext.github.io/mp

1 comments

This 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.