簡體   English   中英

具有多個枚舉的 C++ 運行時類型切換

[英]C++ runtime type switching with multiple enums

這是一個神奇的開關問題, 這里描述了解決方案。 但是,我想知道是否有可能使它成為 3 維的,即讓它基於三個給定的枚舉進行切換。 在理想情況下,當然首選 n 維。 我首先嘗試使它成為二維的,如下所示,但我遇到了雙可變參數解包,我不知道它是如何工作的。

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

調用此代碼將如下所示

enum class abc_enum { a, b, c, COUNT };
enum class defg_enum { d, e, f, g, COUNT };

template<abc_enum e, defg_enum f>
struct stuff {
  void operator()() const {
    std::cout << (int)e << '\n';
    std::cout << (int)f << '\n';
  }
};
magic_switch<abc_enum, defg_enum, stuff>{}(abc_enum::b, defg_enum::f);

但是,由於雙可變參數包擴展,上述內容無法編譯。 對於更多維度,甚至必須解包更多的可變參數包。 n維魔術開關問題有解決方案嗎?

你可以一步一步來

首先將每個運行時枚舉轉換為可能的std::integral_constant std::variant

template <typename Enum, typename Seq> struct EnumVariantHelper;

template <typename Enum, std::size_t ... Is>
struct EnumVariantHelper<Enum, std::index_sequence<Is...>>
{
    using type = std::variant<std::integral_constant<Enum, Enum(Is)>...>;
};

template <typename Enum, std::size_t Count>
using EnumVariant = typename EnumVariantHelper<Enum, std::make_index_sequence<Count>>::type;

template <typename Enum, std::size_t Count>
struct AsVariant
{
    template <Enum E>
    struct F
    {
        EnumVariant<Enum, Count> operator()() const
        {
            return std::integral_constant<Enum, E>{};
        }
    };
};

然后,讓組合的東西std::visit ,你可能會做這樣的事情:

auto v1 = magic_switch<abc_enum, abc_enum::COUNT,
                       AsVariant<abc_enum, std::size_t(abc_enum::COUNT)>::F>{}(abc_enum::b);
auto v2 = magic_switch<defg_enum, defg_enum::COUNT,
                       AsVariant<defg_enum, std::size_t(defg_enum::COUNT)>::F>{}(defg_enum::f);

std::visit([](auto e1, auto e2){ return stuff<e1(), e2()>{}(); }, v1, v2);

演示

您可以將問題拆分為更小的部分並創建一個 ND std::array而不是使用所有可能的函數指針創建單個std::array

這是您詢問的 2D 問題的一個快速而骯臟的示例:

template<class Enum, class Enum2, template <Enum, Enum2> class Z>
struct magic_switch {
  // return value of a call to magic_switch(Args...)
  template <class...Args>
  using R = std::result_of_t<Z<Enum(0), Enum2(0)>(Args...)>;

  // A function pointer for a jump table:
  template <class...Args>
  using F = R<Args...>(*)(Args&&...);

  // Produces a single function pointer for index I and args Args...
  template<size_t I, size_t I2, class...Args>
  F<Args...> f() const {
    using ret = R<Args...>;
    return +[](Args&&...args)->ret{
      using Invoke=Z<Enum(I), Enum2(I2)>;
      return Invoke{}(std::forward<Args>(args)...);
    };
  }

  // builds a jump table:
  // Table for fixed Enum value
  template <class... Args>
  using InnerArray = std::array<F<Args...>, size_t(Enum2::COUNT)>;

  template <size_t I, class... Args, size_t... Is2>
  InnerArray<Args...> innerTable (std::index_sequence<Is2...>) const {
    return {{ f<I, Is2, Args...>()... }}; // Expand Is2.
  }

  // Nested (2D) table
  template<class... Args, size_t... Is>
  std::array<InnerArray<Args...>, size_t(Enum::COUNT)> table (std::index_sequence<Is...>) const {
    return {{ innerTable<Is, Args...>(std::make_index_sequence<size_t(Enum2::COUNT)>{})... }}; // Expand Is.
  }

  template<class...Args>
  R<Args...> operator()(Enum n, Enum2 n2, Args&&...args) {
    // a static jump table for this case of Args...:
    static auto jump=table<Args...>(std::make_index_sequence<size_t(Enum::COUNT)>{});
    // Look up the nth entry in the jump table, and invoke it:
    return jump[size_t(n)][size_t(n2)](std::forward<Args>(args)...);
  }
};

並在您的示例中實際使用它:

enum class abc_enum { a, b, c, COUNT };
enum class defg_enum { d, e, f, g, COUNT };

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

int main () {
  magic_switch<abc_enum, defg_enum, stuff>{}(abc_enum::b, defg_enum::f);
}

我沒有試過讓它自動處理 N 個不同的枚舉。 假設這留給讀者作為練習:)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM