C++ runtime type switching (avoiding switch)

后端 未结 3 1257
感情败类
感情败类 2020-12-09 20:47

I\'ve been into C++ for some years but I have not found yet the solution to a problem I constantly have. Know how to solve it would be awesome.

What I have at the mo

3条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-09 21:33

    This class makes a jump table for a given Enum up to a certain count size based off constructing some template and invoking it with the supplied args. It assumes the enum values start at 0, and go to Count-1.

    templateclass Z>
    struct magic_switch {
      // return value of a call to magic_switch(Args...)
      template
      using R = std::result_of_t(Args...)>;
      // A function pointer for a jump table:
      template
      using F = R(*)(Args&&...);
      // Produces a single function pointer for index I and args Args...
      template
      F f() const {
        using ret = R;
        return +[](Args&&...args)->ret{
          using Invoke=Z;
          return Invoke{}(std::forward(args)...);
        };
      }
      // builds a jump table:
      template
      std::array,size_t(Count)>
      table( std::index_sequence ) const {
        return {{
          f()...
        }};
      }
      template
      R operator()(Enum n, Args&&...args) {
        // a static jump table for this case of Args...:
        static auto jump=table(std::make_index_sequence{});
        // Look up the nth entry in the jump table, and invoke it:
        return jump[size_t(n)](std::forward(args)...);
      }
    };
    

    then if you have an enum:

    enum class abc_enum { a, b, c, count };
    

    and a function object template:

    template
    struct stuff {
      void operator()() const {
        std::cout << (int)e << '\n';
      }
    };
    

    you can dispatch:

    magic_switch{}(abc_enum::b);
    

    in any case, within the template stuff, you get the enum value as a compile time constant. You call it with a run time constant.

    Overhead should be similar to a switch statement, or a vtable call, depending on what the compiler does optimization wise.

    live example.

    Note that setting Enum to std::size_t is valid.

    In C++11 you need make_index_sequence and index_sequence:

    template
    struct index_sequence {};
    namespace details {
      template
      struct sequence_maker : sequence_maker {};
      template
      struct sequence_maker<0,szs...> {
        using type = index_sequence;
      };
    }
    template
    using make_index_sequence=typename details::sequence_maker::type;
    template
    using index_sequence_for=make_index_sequence;
    

    and this alias:

    template
    using result_of_t=typename std::result_of::type;
    

    then strip std:: off their use in the above code.

    live example.

提交回复
热议问题