使用预处理器对C/C++进行元编程

bas*_*ibe 13 c c++ macros coding-style metaprogramming

所以我有这个巨大的树,基本上是一个大的开关/案例,带有字符串键和一个公共对象上的不同函数调用,具体取决于键和一个元数据.

每个条目基本上都是这样的

} else if ( strcmp(key, "key_string") == 0) {
    ((class_name*)object)->do_something();
} else if ( ...
Run Code Online (Sandbox Code Playgroud)

哪里do_something可以有不同的调用,所以我不能只使用函数指针.此外,某些键需要将对象强制转换为子类.

现在,如果我用更高级别的语言编写代码,我会使用lambdas字典来简化这一过程.

在我看来,我可以使用宏来简化这类

case_call("key_string", class_name, do_something());
case_call( /* ... */ )
Run Code Online (Sandbox Code Playgroud)

哪个case_call是将此代码扩展到第一个代码段的宏.

然而,我非常关注这是否会被视为好风格.我的意思是,它会减少打字工作并改善代码的干燥度,但它确实似乎在某种程度上滥用了宏系统.

你会走那条路,还是整理出整条路?你这样做的原因是什么?

编辑

一些澄清:

此代码用作简化脚本API之间的粘合层,该API作为简单的键值属性访问C++ API的几个不同方面.这些属性在C++中以不同的方式实现:有些具有getter/setter方法,有些是在特殊结构中设置的.脚本操作引用C++对象转换为公共基类.但是,某些操作仅适用于某些子类,必须进行转换.

接下来,我可能会更改实际的C++ API,但目前,它必须被视为不可更改.此外,这必须在嵌入式编译器上工作,因此(遗憾的是)boost或C++ 11不可用.

Jon*_*rdy 6

在我看来,适当使用宏.毕竟,它们是为了避免语法重复而制作的.然而,当你有语法重复时,它并不总是语言的错误 - 可能有更好的设计选择可以让你完全避免这个决定.

一般的智慧是使用表映射键来执行操作:

std::map<std::string, void(Class::*)()> table;
Run Code Online (Sandbox Code Playgroud)

然后查找并一次调用该操作:

object->*table[key]();
Run Code Online (Sandbox Code Playgroud)

或者用于find检查失败:

const auto i = table.find(key);
if (i != table.end())
    object->*(i->second)();
else
    throw std::runtime_error(...);
Run Code Online (Sandbox Code Playgroud)

但是如果你说没有函数的共同签名(即你不能使用成员函数指针)那么你实际上该做什么取决于你不知道的项目细节.可能是一个宏是唯一可以忽略你所看到的重复的方法,或者可能是一个更好的方法来实现它.

问问自己:为什么我的函数采用不同的论点?我为什么要使用演员表?如果您要调度对象的类型,则可能需要引入通用接口.


Foz*_*ozi 6

我建议你稍微改变角色.你说这个对象已经是一个知道如何处理某种情况的类了,所以virtual void handle(const char * key)在你的基类中添加一个,让对象检查实现是否适用于它并做任何必要的事情.

这不仅可以消除长if-else-if链,而且还可以更安全,并且可以更灵活地处理这些事件.