ericniebler / meta

A tiny metaprogramming library
Boost Software License 1.0
302 stars 47 forks source link

runtime meta::visit would be nice #57

Open gabyx opened 6 years ago

gabyx commented 6 years ago

I needed a runtime visitor analogue to meta::for_each:

using List = meta::list<int,double,float,double>;
std::size_t index = 2
meta::visit<List>(2, [](auto type){ std::cout << "called with type==double"; });

I came up with this, which builds a static function pointer map:


namespace meta
{
    namespace details
    {
        template<typename Func, typename Type>
        struct Wrapper
        {
            static void invoke(Func f) { f(Type{}); }
        };

        template<typename Func, typename List> struct visit;

        template<typename Func, typename... Types>
        struct visit<Func, meta::list<Types...>>
        {
            static constexpr auto makeMap()
            {
                using FuncPtr = decltype(&Wrapper<Func, void>::invoke);
                using Array   = std::array<FuncPtr, sizeof...(Types)>;
                // Build member function pointers.
                return Array{&Wrapper<Func, Types>::invoke...};
            }
        };
    }  // namespace details

    //! Apply the function `Func` with the type
    //! in the `List` at position `index`.
    template<typename List, typename Func>
    void visit(std::size_t index, Func&& f)
    {
        using F            = decltype(std::forward<Func>(f));  // Define either an lvalue, or rvalue;
        constexpr auto map = details::visit<F, List>::makeMap();
        if(index >= map.size()){ throw std::out_of_range();}
        map[index](std::forward<Func>(f));
    }

}  // namespace meta

Works like a charm for example with an rvalue lambda:

MY_TEST(MetaProgramming, Test1)
{
    using List = meta::list<int, double, short>;
    int b      = -1;
    meta::visit<List>(2,
                      [&](auto type) {
                          using T = decltype(type);
                          if constexpr(std::is_same<T, short>{})
                          {
                              b = 2;
                          }
                      });
    ASSERT_EQ(b, 2) << "Meta Visit failed!";
}
gabyx commented 5 years ago

Could we add this to the library? Is a PR welcome?

gabyx commented 5 years ago

https://wandbox.org/permlink/HYZ4BSj4IpBqMwIn

gabyx commented 5 years ago

See #73