hli*_*ide 4 c++ templates c++11
我有一个包含1223个元素的枚举类型.我有一个1222个案例的函数和一个交换块中的默认案例.如果我想修改枚举类型中的一些元素,我还需要修改该函数.更糟糕的是,我可能有一个具有大开关块的多个功能.所以我试图通过大量的函数来解决它,每个函数根据元素应用正确的动作.因为我也希望进行最小的更改,我希望隐式完成函数指针赋值,所以我使用模板技巧让1223个元素的数组被视为1223个连续的1个元素的子数组的列表来执行通过每个元素的构造函数分配隐式函数指针.
宏是禁止的.包括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
}
Run Code Online (Sandbox Code Playgroud)
因为I_LAST_INSTRUCTION太高了,我在VC2013中遇到了这个错误:
致命错误C1202:递归类型或函数依赖关系上下文过于复杂
编译器似乎接受不超过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> >
> {};
...
Run Code Online (Sandbox Code Playgroud)
Opcode中第二个之后的模板参数用于Instr(id, opd1, opd2, ...)当Opcode描述匹配时匹配有效和指令编码的两个指令操作数.
我有一个很大的开关块,这是一个很大的痛苦:
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;
...
Run Code Online (Sandbox Code Playgroud)
对于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
Run Code Online (Sandbox Code Playgroud)
所以我只需要定义enmental类型的intruction id并为每个指令id定义匹配的操作码.其他一切都是在引擎盖下完成的,除了EncodeInstr和TestInstr中的两个大开关块我必须明确.
使用实体表.
您也可以使用std::map<value, function_pointer>哪个可能更快,具体取决于您的枚举值.
该表也称为跳转表.许多编译器会将switch语句转换为跳转表.尽管表格可能是编译器生成的,但我相信表格更容易维护,然后是大量案例的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.
Run Code Online (Sandbox Code Playgroud)
更强大的版本:将枚举与函数指针相关联.
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;
}
}
Run Code Online (Sandbox Code Playgroud)