13

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

每个条目基本上看起来像这样

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

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

现在,如果我要用更高级的语言编写代码,我会使用 lambda 字典来简化它。

我突然想到我可以使用宏将其简化为类似

case_call("key_string", class_name, do_something());
case_call( /* ... */ )

wherecase_call将是一个宏,可以将此代码扩展为第一个代码片段。

然而,我非常怀疑这是否会被认为是好的风格。我的意思是,它会减少打字工作并提高代码的干性,但它确实似乎在某种程度上滥用了宏系统。

你会沿着那条路走,还是宁愿把整件事都打出来?你这样做的理由是什么?

编辑

一些澄清:

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

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

4

2 回答 2

6

在我看来,这似乎是对宏的适当使用。毕竟,它们是为消除句法重复而设计的。然而,当你有句法重复时,这并不总是语言的错——可能有更好的设计选择可以让你完全避免这个决定。

一般的智慧是使用表将键映射到操作:

std::map<std::string, void(Class::*)()> table;

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

object->*table[key]();

或用于find检查失败:

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

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

问问自己:为什么我的函数采用不同的参数?为什么我使用演员表?如果您要根据对象的类型进行调度,那么您可能需要引入一个通用接口。

于 2012-05-17T15:48:11.647 回答
6

我建议你稍微颠倒一下角色。您是说该对象已经是某个知道如何处理某种情况的类,因此virtual void handle(const char * key)在您的基类中添加 a 并让对象检查实现是否适用于它并执行任何必要的操作。

这不仅会消除冗长的 if-else-if 链,而且还会更安全,并为您处理这些事件提供更大的灵活性。

于 2012-05-17T15:57:02.087 回答