[英]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.