C++ 寻找最优雅的代码调度器
我认为这个问题很普遍。您有一些输入字符串,并且必须根据字符串的内容调用函数。类似于字符串的switch()。 考虑命令行选项 目前我正在使用:C++ 寻找最优雅的代码调度器,c++,c,design-patterns,coding-style,C++,C,Design Patterns,Coding Style,我认为这个问题很普遍。您有一些输入字符串,并且必须根据字符串的内容调用函数。类似于字符串的switch()。 考虑命令行选项 目前我正在使用: using std::string; void Myclass::dispatch(string cmd, string args) { if (cmd == "foo") cmd_foo(args); else if (cmd == "bar") cmd_bar(args); else if .
using std::string;
void Myclass::dispatch(string cmd, string args) {
if (cmd == "foo")
cmd_foo(args);
else if (cmd == "bar")
cmd_bar(args);
else if ...
...
else
cmd_default(args);
}
void Myclass::cmd_foo(string args) {
...
}
void Myclass::cmd_bar(string args) {
...
}
在标题中
class Myclass {
void cmd_bar(string args);
void cmd_foo(string args);
}
所以每一个foo和bar我都要重复四(4!)次。我知道我可以先将函数指针和字符串馈送到静态数组,然后在循环中进行调度,从而保存一些if…else行。但是,是否存在一些宏欺骗(或预处理器滥用,取决于POV),使得以某种方式定义函数并同时自动更新数组成为可能?
所以我只需要写两次,或者如果内联使用,可能只写一次
我正在寻找C或C++的解决方案。 < P>听起来好像你在寻找 大概是这样的: 创建一个像这样的地图
std::map<std::string, Command*> myMap;
您可以构建字符串->函数指针的哈希表:
typedef void (*cmd)(string);
丑陋的宏解决方案,这是你想要的。请注意,它不会自动注册,但会使某些内容保持同步,如果只添加到映射,而不添加源文件中的函数,还会导致编译错误 A.h:
// Note: no fileguard
// The first is the text string of the command,
// the second is the function to be called,
// the third is the description.
UGLY_SUCKER( "foo", cmd_foo, "Utilize foo." );
UGLY_SUCKER( "bar", cmd_bar, "Turn on bar." );
Parser.h:
class Myclass {
...
protected:
// The command functions
#define UGLY_SUCKER( a, b, c ) void b( args )
#include Mappings.h
#undef UGLY_SUCKER
};
Parser.cpp:
void Myclass::dispatch(string cmd, string args) {
if (cmd == "")
// handle empty case
#define UGLY_SUCKER( a, b, c ) else if (cmd == a) b( args )
#include Mappings.h
#undef UGLY_SUCKER
else
cmd_default(args);
}
void Myclass::printOptions() {
#define UGLY_SUCKER( a, b, c ) std::cout << a << \t << c << std::endl
#include Mappings.h
#undef UGLY_SUCKER
}
void Myclass::cmd_foo(string args) {
...
}
void Myclass::dispatch(字符串cmd,字符串args){
如果(cmd==“”)
//处理空箱
#如果(cmd==a)b(args),则定义(a,b,c)else
#包括映射
#丑陋的傻瓜
其他的
cmd_默认值(args);
}
void Myclass::printOptions(){
#定义(a,b,c)std::cout根据问题注释中的“我的链接”,基本解决方案是将字符串映射到某种函数调用
要实际注册字符串->函数指针/函子对,请执行以下操作:
首先,创建一个单例(震惊!恐怖!)调度程序对象。
让我们称之为Dispatcher——它是地图的包装,其中
Func是函数指针或函子类型
然后,创建一个register类:
struct Register {
Register( comst string & s, Func f ) {
TheDispatcher.Add( s, f );
}
};
现在在您创建的各个编译单元中
静态对象(震惊!恐怖!):
将创建这些对象(假设代码不在静态库中),并将自动向Dispatcher注册
在运行时,您可以在Dispatcher中查找字符串并执行关联的函数/functor。您必须至少定义函数并将它们添加到某个注册表中。(如果它们是某个类的非内联成员函数,您还必须声明它们。)而不是生成实际代码的特定于域的语言(比如)我认为没有办法提及这两个(或三个)功能《泰晤士报》。这个问题已经被反复问过了,我在这里回答了。他还要求某种注册计划,以减少维护词典的工作量。他是的,但我认为没有一个能让他满意。你可以用宏做一些粗糙和丑陋的事情,但这比它的价值更麻烦。不,这是问题我使用了一个名为“cmd_*的函数”但它们可能是不同的,不需要表示命令。另外,dispatcher不需要绑定在对象中。@drhirsch。它们不必是实际的命令。这只是模式的名称。基本上,这是一种基于输入执行一些预定义代码的方法。我认为Neil和Glen ar的解决方案E建议是正确的答案。它不包括自注册,但我不确定C++中有一种干净的方式。在爪哇或C,它是非常可行的。@ Steven You可能在每个命令类中使用某种形式的静态初始化进行自注册。尽管如此,您可能需要使您的STD::Map是一个单例。就像你说的,它不是很干净它不需要干净,请注意,我明确表示asekd用于预处理器滥用。但是这个解决方案看起来很有趣,它确实给了我一些新的想法。但是现在我需要CommandFoo和CommandBar类声明和构造函数,所以我再次重复Foo和Bar四次。当然,它是丑陋的,但是它似乎把重复减少到了两个。但是在代码中间包含一个非常丑陋的。+ 1 + 1,出于同样的原因。同样,假设每个命令“FoO”导致调用CMDYFO,有一点严格化,你也可以避免重复这些参数。是的,我在沿着“粘贴”的线条思考。运算符到。这不是模式的实际替代方案:它是相同的概念,不同的实现方式。是的,我想我终于明白了这一点。
void Myclass::dispatch(string cmd, string args) {
if (cmd == "")
// handle empty case
#define UGLY_SUCKER( a, b, c ) else if (cmd == a) b( args )
#include Mappings.h
#undef UGLY_SUCKER
else
cmd_default(args);
}
void Myclass::printOptions() {
#define UGLY_SUCKER( a, b, c ) std::cout << a << \t << c << std::endl
#include Mappings.h
#undef UGLY_SUCKER
}
void Myclass::cmd_foo(string args) {
...
}
struct Register {
Register( comst string & s, Func f ) {
TheDispatcher.Add( s, f );
}
};
Register r1_( "hello", DoSayHello );