【问题标题】:Looking for the most elegant code dispatcher寻找最优雅的代码调度器
【发布时间】:2009-08-19 16:19:46
【问题描述】:

我认为这个问题很常见。您有一些输入字符串,并且必须根据字符串的内容调用一个函数。类似于字符串的 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 ...
        ...
    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++ 的解决方案。

【问题讨论】:

  • 这个问题被反复问过,我在这里回答了stackoverflow.com/questions/659581/…
  • 他还要求某种注册方案以减少维护字典的工作量。
  • 他是,但我认为没有一个能让他满意。你可以用宏做一些粗糙和丑陋的事情,但它的麻烦多于它的价值。

标签: c++ c design-patterns coding-style


【解决方案1】:

听起来您正在寻找Command pattern

类似这样的:

创建这样的地图

std::map<std::string, Command*> myMap;

然后只需使用您的密钥执行这样的命令....

std::map<std::string, Command*>::iterator it = myMap.find(str);
if( it != myMap.end() ) {
    it->second->execute()
}

要注册您的命令,您只需这样做

myMap["foo"] = new CommandFoo("someArgument");
myMap["bar"] = new CommandBar("anotherArgument");

【讨论】:

  • 没有。我使用了名为“cmd_*”的函数可能会产生误导,但它们可能有所不同,不需要表示命令。另外,调度程序不需要绑定在对象中。
  • @drhirsch。它们不必是实际命令。这只是模式的名称。基本上它是一种基于输入执行一些预定义代码的方式
  • 我认为 Neil 和 Glen 建议的解决方案是正确的答案。它不包括自我注册,但我不确定在 C++ 中是否有一种干净的方式来完成它。不过,在 Java 或 C# 中,这将是非常可行的。
  • @Steven 您可以在每个命令类中使用某种形式的静态初始化来进行自我注册。虽然那时你可能需要让你的 std::map 成为单身人士。就像你说的,不是很干净
  • 它不需要很干净,注意我明确要求预处理器滥用。但是这个解决方案看起来很有趣,它确实给了我一些新的想法。但是现在我需要 CommandFoo 和 CommandBar 类的声明和构造函数,所以我又重复了四次 Foo 和 Bar。
【解决方案2】:

根据我在问题评论中的链接,基本解决方案是将字符串映射到某种函数调用。

实际注册字符串 -> 函数指针/函子对:

首先,有一个单例(震惊!恐怖!)调度程序对象。 我们称它为 TheDispatcher - 它是 map&lt;string,Func&gt; 的包装器,其中 Func 是您的函数指针或函子类型。

然后,有一个寄存器类:

struct Register {
   Register( comst string & s, Func f ) {
      TheDispatcher.Add( s, f );
   }
};

现在在您创建的各个编译单元中 静态物体(震惊!恐怖!):

Register r1_( "hello", DoSayHello );

这些对象将被创建(假设代码不在静态库中)并将自动注册到 TheDispatcher。

在运行时,您在 TheDispatcher 中查找字符串并执行相关的函数/仿函数。

【讨论】:

    【解决方案3】:

    as alternative to the Command pattern 你可以建立一个字符串散列表 -> 函数指针

    typedef void (*cmd)(string);
    

    【讨论】:

    • 这不是模式的实际替代方案:它是相同的概念,不同的实现方式。
    【解决方案4】:

    丑陋的宏解决方案,你有点要求。请注意,它不会自动注册,但它确实会保持一些东西同步,如果您只添加到映射而不是源文件中的函数,也会导致编译错误。

    映射.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." );
    

    解析器.h:

    class Myclass {
    ...
    protected:
        // The command functions
        #define UGLY_SUCKER( a, b, c ) void b( args )
        #include Mappings.h
        #undef UGLY_SUCKER
    };
    

    解析器.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) {
    ...
    }
    

    【讨论】:

    • 好吧,当然,这很难看,但似乎将重复次数减少到两次。但是代码中间的#include 真的很难看。 +1
    • +1,出于同样的原因。此外,假设每个命令“foo”都会导致调用 cmd_foo,通过一些字符串化,您也可以避免参数中的重复。
    • 是的,我正在考虑“粘贴”运算符的思路。
    【解决方案5】:

    您至少必须定义函数并将它们添加到某个注册表中。 (如果它们是某个类的非内联成员函数,您还必须声明它们。)除了一些特定于域的语言生成实际代码(如cjhuitt's macro hackery),我认为没有办法提及这些功能两次(或三次)。

    【讨论】:

    • 是的,我想我终于把它塞进了我厚厚的脑袋里。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-30
    • 2013-07-30
    • 1970-01-01
    • 2011-04-03
    • 2016-09-20
    • 1970-01-01
    相关资源
    最近更新 更多