c++ - 从一个巨大的枚举中,我尝试通过使用一些模板技巧来创建一个函数来应用正确的操作而不使用开关主体

标签 c++ templates c++11

我有一个包含 1223 个元素的 enum 类型。我有一个包含 1222 个案例的函数和一个开关 block 中的默认案例。如果我想修改 enum 类型中的一些元素,我也需要修改那个函数。更糟糕的是,我可能有不止一种功能,而且有一个大开关 block 。所以我试图通过一大堆函数来解决它,每个函数都根据元素应用正确的 Action 。因为我也想做最少的更改,所以我想隐式完成函数指针赋值,所以我使用了一个模板技巧,让 1223 个元素的数组被视为 1 个元素的 1223 个连续子数组的列表来执行通过每个元素的构造函数进行隐式函数指针分配。

禁止使用宏。包括 Boost 在内的外部库也被禁止。

这是一个简化的代码(如果 I_LAST_INSTRUCTION 值低得多,则可编译和运行):

#include <iostream>
#include <vector>

using namespace std;

typedef size_t Instr; // dummy one for simplified code

enum
{
    I_INVALID = 0,

    I_LAST_INSTRUCTION = 1223
};

template< size_t id >
static void Test$(std::vector< Instr > &, bool)
{
    cout << "testing instruction #" << id << endl;
}

template< typename Derived, size_t start_id, size_t end_id >
struct Tester$ : Tester$ < Derived, start_id, end_id - 1 >
{
    Tester$()
    {
        static_cast<Derived *>(this)->array[end_id - 1] = Test$< end_id - 1 >;
    }
};

template< typename Derived >
struct Tester$ < Derived, 0, 0 >
{
};

struct Tester : Tester$< Tester, I_INVALID, I_LAST_INSTRUCTION >
{
    void(*array[size_t(I_LAST_INSTRUCTION)])(std::vector< Instr > & list, bool is64);

    void operator()(size_t id, std::vector< Instr > & list, bool is64)
    {
        if (id < I_LAST_INSTRUCTION)
        {
            (array[size_t(id)])(list, is64);
        }
        else
        {
            // to do nothing
        }
    }
};

int main()
{
    Tester tester;

    std::vector< Instr > list;

    tester(0, list, true); // display testing instruction #0
    tester(1, list, true); // display testing instruction #1
    tester(2, list, true); // display testing instruction #2
    tester(8, list, true); // display testing instruction #8
    tester(1222, list, true); // display testing instruction #1222
    tester(1223, list, true); // invalid instruction number - do nothing
}

因为 I_LAST_INSTRUCTION 太高了,我用 VC2013 遇到了这个错误:

fatal error C1202: recursive type or function dependency context too complex

编译器似乎接受的嵌套类模板实例不超过 499 个。

我能看到的解决方案是将嵌套类模板实例定义为二叉树,使其最大深度接近 log2(n) 而不是列表(其最大深度为 n)。

所以我的问题是如何有效地将元列表实现为元二叉树以使编译器满意?

编辑:另一种解决方案是使用每个子数组包含更多元素的列表,以将深度列表除以子数组中的最大元素数。每个子数组使用 4 个元素解决了我遇到的问题。

编辑 2:关于我为什么选择这种方式的更多细节

我的指令是通过模板类组合来描述的:

namespace x86
{
    namespace encoder
    {
        // Group 8086+
        template<> struct Opcode$< I_AAA > : Opcode < I_AAA, 0x00000037, Gw < RW >, DummyRw< 0, AX >, i64 > {};

        template<> struct Opcode$< I_AAD > : Opcode < I_AAD, 0x000000D5, Gw_Ib < RW >, DummyRw< 0, AX >, i64 > {};

        template<> struct Opcode$< I_AAM > : Opcode < I_AAM, 0x000000D4, Gw_Ib < RW >, DummyRw< 0, AX >, i64 > {};

        template<> struct Opcode$< I_AAS > : Opcode < I_AAS, 0x0000003F, Gw < RW >, DummyRw< 0, AX >, i64 > {};

        template<> struct Opcode$< I_ADC > :
            Switch
            <
            /**/ Opcode < I_ADC, 0x00000012, Gb_Eb  < RW, R >, OSb             >,
            /**/ Opcode < I_ADC, 0x00000013, Gw_Ew  < RW, R >, OSw             >,
            /**/ Opcode < I_ADC, 0x00000013, Gd_Ed  < RW, R >, OSd             >,
            /**/ Opcode < I_ADC, 0x00000013, Gq_Eq  < RW, R >, OSq             >,
            /**/ Opcode < I_ADC, 0x00000010, Eb_Gb  < RW, R >, OSb             >,
            /**/ Opcode < I_ADC, 0x00000011, Ew_Gw  < RW, R >, OSw             >,
            /**/ Opcode < I_ADC, 0x00000011, Ed_Gd  < RW, R >, OSd             >,
            /**/ Opcode < I_ADC, 0x00000011, Eq_Gq  < RW, R >, OSq             >,
            /**/ Opcode < I_ADC, 0x00000014, AL_Ib  < RW    >                  >,
            /**/ Opcode < I_ADC, 0x00000080, Eb_Ib  < RW    >, OSb, Group1 <2> >,
            /**/ Opcode < I_ADC, 0x00000083, Ew_Ib  < RW    >, OSw, Group1 <2> >,
            /**/ Opcode < I_ADC, 0x00000083, Ed_Ib  < RW    >, OSd, Group1 <2> >,
            /**/ Opcode < I_ADC, 0x00000083, Eq_Ib  < RW    >, OSq, Group1 <2> >,
            /**/ Opcode < I_ADC, 0x00000015, AX_Iw  < RW    >, OSw             >,
            /**/ Opcode < I_ADC, 0x00000015, EAX_Id < RW    >, OSd             >,
            /**/ Opcode < I_ADC, 0x00000015, RAX_Id < RW    >, OSq             >,
            /**/ Opcode < I_ADC, 0x00000081, Ew_Iw  < RW    >, OSw, Group1 <2> >,
            /**/ Opcode < I_ADC, 0x00000081, Ed_Id  < RW    >, OSd, Group1 <2> >,
            /**/ Opcode < I_ADC, 0x00000081, Eq_Id  < RW    >, OSq, Group1 <2> >
            > {};
    ...

Opcode 中第二个之后的模板参数用于匹配有效 Instr(id, opd1, opd2, ...) 的指令操作数和 Opcode 描述匹配时的指令编码。

我有一个很大的开关 block ,这很痛苦:

void Backend::EncodeInstr(Instr & instr)
{
    switch (instr.id_)
    {
    case I_AAA:                 JITASM_ASSERT(encoder::Opcode$< I_AAA >::Encode(instr, is64_)); break;
    case I_AAD:                 JITASM_ASSERT(encoder::Opcode$< I_AAD >::Encode(instr, is64_)); break;
    case I_AAM:                 JITASM_ASSERT(encoder::Opcode$< I_AAM >::Encode(instr, is64_)); break;
    case I_AAS:                 JITASM_ASSERT(encoder::Opcode$< I_AAS >::Encode(instr, is64_)); break;
    case I_ADC:                 JITASM_ASSERT(encoder::Opcode$< I_ADC >::Encode(instr, is64_)); break;
    ...

对于 Testinstr 也是如此(它的目的是生成一个与所有操作码匹配的指令列表,以检查编码器是否正确)。例如,TestInstr(I_XOR) 将给出:

0x10000000( 2):                 DA32 xor              bl, dl
0x10000002( 6):         555555551D32 xor              bl, byte ptr [0x55555555]
0x10000008( 3):               DA3366 xor              bx, dx
0x1000000B( 7):       555555551D3366 xor              bx, word ptr [0x55555555]
0x10000012( 2):                 DA33 xor              ebx, edx
0x10000014( 6):         555555551D33 xor              ebx, dword ptr [0x55555555]
0x1000001A( 2):                 DA32 xor              bl, dl
0x1000001C( 6):         555555551530 xor              byte ptr [0x55555555], dl
0x10000022( 3):               DA3366 xor              bx, dx
0x10000025( 7):       55555555153166 xor              word ptr [0x55555555], dx
0x1000002C( 6):         555555551531 xor              dword ptr [0x55555555], edx
0x10000032( 2):                 5534 xor              al, 0x55
0x10000034( 3):               55F380 xor              bl, 0x55
0x10000037( 7):       55555555553580 xor              byte ptr [0x55555555], 0x55
0x1000003E( 4):             55F38366 xor              bx, 0x55
0x10000042( 8):     5555555555358366 xor              word ptr [0x55555555], 0x55
0x1000004A( 3):               55F383 xor              ebx, 0x55
0x1000004D( 7):       55555555553583 xor              dword ptr [0x55555555], 0x55
0x10000054( 4):             55553566 xor              ax, 0x5555
0x10000058( 5):           5555555535 xor              eax, 0x55555555
0x1000005D( 5):           5555F38166 xor              bx, 0x5555
0x10000062( 9):   555555555555358166 xor              word ptr [0x55555555], 0x5555
0x1000006B( 6):         55555555F381 xor              ebx, 0x55555555
0x10000071(10): 55555555555555553581 xor              dword ptr [0x55555555], 0x55555555

所以我只需要定义enum类型的指令id,并为每个指令id定义匹配的操作码。除了我必须明确指出的 EncodeInstr 和 TestInstr 中的两个大开关 block 外,其他一切都在幕后完成。

最佳答案

使用实体表。

你也可以使用 std::map<value, function_pointer>这可能会更快 取决于您的枚举值。

该表也称为跳转表。许多编译器会转换 switch语句进入跳转表。虽然 table of 可能是编译器生成的,但我相信对于大量情况,该表比 switch 语句更容易维护。

编辑 1:- 示例
简单版:函数指针数组。

// Synonym for pointer to a function that has no parameters
//    and returns no values.
typedef void (*Function_Pointer)(void);

// Prototypes
void Eat(void);
void Sleep(void);
void Drink(void);

// The table
const static Function_Ptr dispatch_table[] =
{ /* Index 0 */ Eat,
  /* Index 1 */ Sleep,
  /* Index 2 */ Drink,
};

// Execution syntax
unsigned int index = 1;
(dispatch_table[index])();  // Execute Sleep() function.

更健壮的版本:将枚​​举与函数指针相关联。

struct Dispatch_Entry
{
  unsigned int  function_ID;
  Function_Pointer p_function;
};

const static Dispatch_Entry robust_dispatch_table[] =
{
  // Unlike the array, this structure allows the
  // function pointers to be listed in any order.
  // Also, they don't have to be contiguous.
  {2, Drink},
  {0, Eat},
  {1, Sleep},
};
const unsigned int num_entries =
  sizeof(robust_dispatch_table) / sizeof(robust_dispatch_table[0]);

// Look up the function:
for (unsigned int i = 0;
     i < num_entries;
     ++i)
{
  if (robust_dispatch_table[i].function_ID == function_id_to_execute)
  {
    (robust_dispatch_table[i].p_function)(); // Execute function.
    break;
  }
}

关于c++ - 从一个巨大的枚举中,我尝试通过使用一些模板技巧来创建一个函数来应用正确的操作而不使用开关主体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28004950/

相关文章:

c++ - 完美转运不取货方式

c++11 - 为什么是 int* arr = {};合法的?

C++ 二次方程求解器 - 不正确的虚数解

c++ - 为什么类型推导在(非指针)函数类型上失败

c++ - 类型名称的概念/SFINAE 错误

c++ - Boost Intrusive unordered_set 在 C++11 模式下使用 GCC 在 1.48 中被破坏

c++ - 为什么我的 fortran 例程将不正确的值传递给我的 C++ 函数?

c++ - 使用 typedef 声明函数有什么意义?

C++将无穷大转换为最大值并将-inf转换为最小值

c++ - C# TPL 比 C++ PPL 更快?