簡體   English   中英

如何簡潔地實例化多個枚舉的模板?

[英]How can I instantiate template of multiple enums concisely?

如果我想為模板 function Caclculate實例化 Type1、Type2 和 Type3 的所有組合,看來我必須編寫 72 行代碼。 有沒有辦法簡化這些代碼?

#include <iostream>
#include <map>
#include <tuple>
#include <functional>

enum Type1 {
    kType1_1,
    kType1_2,
    kType1_3,
    kType1_4,
    kType1_5,
    kType1_6,
};

enum Type2 {
    kType2_1,
    kType2_2,
    kType2_3,
};

enum Type3 {
    kType3_1,
    kType3_2,
    kType3_3,
    kType3_4,
};

template <Type1 t1, Type2 t2, Type3 t3>
int Caclculate() {
    std::cout << static_cast<int32_t>(t1) << " " << static_cast<int32_t>(t2) << std::endl;
    return 0;
}

int CalculateHandler(Type1 t1, Type2 t2, Type3 t3) {
    static std::map<std::tuple<Type1, Type2, Type3>, std::function<int()>> func = {
        std::make_pair(std::make_tuple(Type1::kType1_1, Type2::kType2_1, Type3::kType3_1), Caclculate<Type1::kType1_1, Type2::kType2_1, Type3::kType3_1>),
        std::make_pair(std::make_tuple(Type1::kType1_1, Type2::kType2_2, Type3::kType3_1), Caclculate<Type1::kType1_1, Type2::kType2_2, Type3::kType3_1>),
    };
    return func[std::make_tuple(t1, t2, t3)]();
}

int main() {
    CalculateHandler(kType1_1, kType2_2, kType3_1);
}

使用 for(int i;...) 然后,每次使用每個 <> 來獲得一個新變量。 那么你的代碼會更短。 注意:你必須在 for{} 中使用 <>; 否則它將無法正常工作。 如果您需要更多說明,請與我聯系。

你可以這樣做,但一般的解決方案可能比僅僅輸入它要長。

我們想將枚舉插入到模板 arguments 並得到一個組合列表:

using Input1 = Sequence<Type1_1, Type1_2>;
using Input2 = Sequence<Type2_1, Type2_2>;
using Input3 = Sequence<Type3_1, Type3_2>;

using Output = Combine<Input1, Input2, Input3>;
/* result: CombinationSet<
    Combination<Type1_1, Type2_1, Type3_1>,
    Combination<Type1_1, Type2_1, Type3_2>,
    Combination<Type1_1, Type2_2, Type3_1>,
    // etc
*/

因此,首先,定義輸入和 output 類型:

template <typename T, T... Cs>
struct Sequence {
    using Type = T; //
};

// represents one iteration of Combined lists
template <typename, typename, typename>
struct Combination;

// a set of Combinations
template <typename... Ts>
struct CombinationSet;

現在,可以定義第一級:將兩個常量和一個序列轉換為一組組合。 我們可以通過偏特化推導出常量和序列的類型:

template <typename, typename, typename>
struct CombineImpl;

template <typename T1, typename T2, typename T3, T1 C1, T2 C2, T3... C3s>
struct CombineImpl<std::integral_constant<T1, C1>, std::integral_constant<T2, C2>, Sequence<T3, C3s...>>
{
    using Type = CombinationSet<Combination<std::integral_constant<T1, C1>, std::integral_constant<T2, C2>, std::integral_constant<T3, C3s>>...>;
};

/* example: 
       CombineImpl<
           std::integral_constant<Type1, Type1_1>,
           std::integral_constant<Type2, Type2_1>,
           Sequence<Type3, Type3_1, Type3_2>
       >
   gets turned into:
       CombinationSet<
           Combination<
               std::integral_constant<Type1, Type1_1>,
               std::integral_constant<Type2, Type2_1>,
               std::integral_constant<Type2, Type2_1>
           >,
           Combination<
               std::integral_constant<Type1, Type1_1>,
               std::integral_constant<Type2, Type2_1>,
               std::integral_constant<Type2, Type2_2>
           >
       >
*/

現在下一個級別:如果我們有一個常量和兩個序列,我們可以多次調用CombineImpl<C, C, S>並得到一堆CombinationSet 但是,我們需要一種連接所有集合的方法:

template <typename T1, typename T2, typename T3, T1 I1, T2... I2s, T3... I3s>
struct CombineImpl<std::integral_constant<T1, I1>, Sequence<T2, I2s...>, Sequence<T3, I3s...>> 
{
    using Type = Merge<typename CombineImpl<std::integral_constant<T1, I1>, std::integral_constant<T2, I2s>, Sequence<T3, I3s...>>::Type...>;
              // ^^^^^ how to implement this?
};

我只能想到使用遞歸類型來展平集合。 可能有更好的log n解決方案,但我不夠聰明,無法做到:

// stub - the first type is the output set, followed by input sets
template <typename... Ts>
struct MergeImpl;

// recursive bit - deduce the combinations in the next set, add to output
template <typename... TOut, typename... Ts, typename... TIn>
struct MergeImpl<CombinationSet<TOut...>, CombinationSet<Ts...>, TIn...> 
{
    using Type = typename MergeImpl<CombinationSet<TOut..., Ts...>, TIn...>::Type;
};

// terminal - when there are no more inputs, expose the output.
template <typename... TOut>
struct MergeImpl<CombinationSet<TOut...>>
{
    using Type = CombinationSet<TOut...>;
};

// type alias to start with an empty output set
template <typename... Ts>
using Merge = typename MergeImpl<CombinationSet<>, Ts...>::Type;

/* example: 
       Merge<
           CombinationSet<
               Combination<
                   std::integral_constant<Type1, Type1_1>,
                   std::integral_constant<Type2, Type2_1>,
                   std::integral_constant<Type2, Type2_1>
               >,
               Combination<
                   std::integral_constant<Type1, Type1_1>,
                   std::integral_constant<Type2, Type2_1>,
                   std::integral_constant<Type2, Type2_2>
               >
           >,
           CombinationSet<
               Combination<
                   std::integral_constant<Type1, Type1_1>,
                   std::integral_constant<Type2, Type2_2>,
                   std::integral_constant<Type2, Type2_1>
               >
           >
        >
    Gets turned into:
        CombinationSet<
           Combination<
               std::integral_constant<Type1, Type1_1>,
               std::integral_constant<Type2, Type2_1>,
               std::integral_constant<Type2, Type2_1>
           >,
           Combination<
               std::integral_constant<Type1, Type1_1>,
               std::integral_constant<Type2, Type2_1>,
               std::integral_constant<Type2, Type2_2>
               >
           Combination<
               std::integral_constant<Type1, Type1_1>,
                   std::integral_constant<Type2, Type2_2>,
                   std::integral_constant<Type2, Type4_1>
               >
        > 

現在定義了合並,我們可以添加另一個層來獲取三個序列的所有組合:

template <typename T1, typename T2, typename T3, T1... I1s, T2... I2s, T3... I3s>
struct CombineImpl<Sequence<T1, I1s...>, Sequence<T2, I2s...>, Sequence<T3, I3s...>>
{
    using Type = Merge<typename CombineImpl<std::integral_constant<T1, I1s>, Sequence<T2, I2s...>, Sequence<T3, I3s...>>::Type...>;
};

template <typename T1, typename T2, typename T3>
using Combine = typename CombineImpl<T1, T2, T3>::Type;

下一步是從CombinationSet創建 map。 我將再次使用部分專業化:

// stub
template <typename>
struct CalculateHandlerImpl;

// specialization - deduce combination types and declare static map
template <typename T1, typename T2, typename T3, T1... C1s, T2... C2s, T3... C3s>
struct CalculateHandlerImpl<CombinationSet<Combination<std::integral_constant<T1, C1s>, std::integral_constant<T2, C2s>, std::integral_constant<T3, C3s>>...>> {
    static std::map<std::tuple<T1, T2, T3>, std::function<int()>> value;
};

// static maps have to be defined out of line
template <typename T1, typename T2, typename T3, T1... C1s, T2... C2s, T3... C3s>
std::map<std::tuple<T1, T2, T3>, std::function<int()>> CalculateHandlerImpl<CombinationSet<Combination<std::integral_constant<T1, C1s>, std::integral_constant<T2, C2s>, std::integral_constant<T3, C3s>>...>>::value = {
    { std::make_tuple(C1s, C2s, C3s), Calculate<C1s, C2s, C3s> }...
};

最后,編寫一個處理程序 function 將它們捆綁在一起:

template <typename T1, typename T2, typename T3>
int CalculateHandler(typename T1::Type t1, typename T2::Type t2, typename T3::Type t3) {
    return CalculateHandlerImpl<Combine<T1, T2, T3>>::value[std::make_tuple(t1, t2, t3)]();
}

// maybe a variadic macro can help define this and the enum at the same time
using Type1Seq = Sequence<Type1, 
    kType1_1, 
    kType1_2, 
    kType1_3, 
    kType1_4, 
    kType1_5, 
    kType1_6>;

using Type2Seq = Sequence<
    Type2, 
    kType2_1, 
    kType2_2, 
    kType2_3>;

using Type3Seq = Sequence<Type3,
    kType3_1,
    kType3_2,
    kType3_3,
    kType3_4>;

int main() {
    CalculateHandler<Type1Seq, Type2Seq, Type3Seq>(kType1_1, kType2_2, kType3_1);
}

https://godbolt.org/z/r1f4886dh

現在,您會注意到所有這些模板代碼可能至少有 72 行。 另外,它肯定會增加編譯時間。 但是,如果列表增長,上述內容會有所幫助。 它可以省去檢查是否包含所有排列的麻煩。

暫無
暫無

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

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