Skip to content

runtime meta::visit would be nice #57

@gabyx

Description

@gabyx

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!";
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions