我有一个包含 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/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po