【问题标题】:How can I call a method given only its name?我怎样才能调用一个只给它名字的方法?
【发布时间】:2010-12-29 15:57:59
【问题描述】:

我正在尝试使用方法 void run( string method ),它将在该类中运行 method。例如:

class Foo {
    public:
    void run( string method ) {
        // this method calls method *method* from this class
    }
    void bar() {
        printf( "Function bar\n" );
    }
    void foo2() {
        printf( "Function foo2\n" );
    }
}

Foo foo;

int main( void ) {
    foo.run( "bar" );
    foo.run( "foo2" );
}

这将打印:

Function bar
Function foo2

谢谢! :)

【问题讨论】:

  • 你想完成什么?

标签: c++ class methods


【解决方案1】:

您可以创建一个std::map,它将字符串映射到成员函数指针,只要您要调用的所有函数都具有相同的签名。

地图将被声明为:

 std::map<std::string, void(Foo::*)()> function_map;

【讨论】:

  • 否则,if(){}else if(){}else if(){} else{} 还在这里 ;-)
  • 不过,如果我要采用此解决方案,我会将其设为静态地图 - 永远不要将编译时可以做的事情推迟到运行时。
【解决方案2】:

正如其他人所指出的,C++ 并没有开箱即用地进行这种反射(很可能这不是您想要做的。)

但我会提到确实存在一些预处理器,它们为类类型和方法的子集实现此功能。 Qt 的 moc 就是一个例子,它是信号/槽机制的一部分。注意method()methodCount()QMetaObject ...

Qt 这样做的方式是将一个工具注入到您的构建过程中,该工具可以生成表格并将它们与您的其余代码一起编译。这都是你可以手写的东西——不是语言功能。

【讨论】:

    【解决方案3】:

    已经提到的地图解决方案适用于固定的函数列表,但我认为在 C++ 中通常没有这样的内省方法。也就是说,您不能有一个函数 void perform( string methodName ) 说“我有一个名为 methodName 的方法吗?如果有,请调用它。”

    【讨论】:

    • 是的,C++ 在这方面完全不像 Java。
    • @benzado:Objective-C 与您发送消息时完全不同。如果对象知道如何对消息做出反应,它将执行代码。 Python/Perl/Javascript:所有对象都只是映射,因此方法只是作为值附加的对象。 C# 和 Java 的不同之处在于允许类型自省(AKA 反射),因此允许您按名称查找方法然后调用它们。
    • @benzado:续。使用映射作为所有对象的基础会使类型安全成为一场噩梦。 Java/C# 反射对于开发工具构建器(和一些库)来说非常棒。但是在实际应用中使用反射会导致代码脆弱。
    • @MartinYork:如果你说 Objective-C 不提供运行时内省,那你就错了(见class_copyMethodList() 和朋友们)。如果您想选择强类型系统与弱类型系统之争,我不感兴趣。否则,我不确定您要表达什么观点。
    • @benzado,我鄙视 Java,但不管好坏,这就是他们这些天在学校教的东西,所以我认为原始海报最熟悉的东西。
    【解决方案4】:

    虽然前面的答案适用于您的解决方案,但您的解决方案看起来并不适合您的问题。

    我有两个建议:1) 构建一个更好的抽象基类或 2) 为仿函数使用基类(函数对象)。让我们更详细地看看它们......

    给定一个模拟处理器指令集的项目,我们可以模拟执行:

    struct Instruction_Interface
    {
      virtual void execute(void) = 0;
    };
    
    typedef std::vector<Instruction_Interface *> Instruction_Container;
    
    //...
    Instruction_Container::iterator iter;
    Instruction_Container program_instructions;
    //...
    for (iter =  program_instructions.begin();
         iter != program_instructions.end();
         ++iter)
    {
      (*iter)->execute(); // Execute the instruction;
    }
    

    这允许调用每条指令的execute 方法,而不管指令的种类。可以向接口添加更多方法;在此示例中,可以添加“toString”,将指令转换为文本。

    Idea 2:  Functors 
    

    为您要使用的功能建立一个基类或接口。将它们放入容器并迭代容器。迭代也可以是有条件的:

    struct Functor_Interface
    {
       virtual std::string  get_name(void) const = 0;
       virtual void         execute(void) = 0;
       virtual void         execute_if_name(const std::string& name)
       { if (name == get_name())
         {
            execute();
         }
       }
    };
    
    typedef std::vector<Functor_Interface *> Functor_Container;
    //...
    Functor_Container the_functors;
    //...
    Functor_Container::iterator iter;
    for (iter =  the_functors.begin();
         iter != the_functors.end();
         ++iter)
    {
       (*iter)->execute_if_name("loader"); // Execute the functor if it is a *loader*.
       (*iter)->execute_if_name("math");
    }
    

    总而言之,请仔细考虑您的设计,看看是否有更好的流程不需要函数名称,而是让函数决定执行或一般执行盲方法。

    【讨论】:

      【解决方案5】:

      您可以制作成员函数指针的映射,映射的键是函数的名称。不过,指向成员函数的指针有点难以使用。

      http://www.parashift.com/c++-faq-lite/pointers-to-members.html

      更简单的方法是在 run 函数中执行一堆 if-then-else 块来调用适当的函数。

      【讨论】:

        【解决方案6】:

        正如其他发帖人所说,您需要在 C++ 中手动执行此操作,因为该语言没有内置 Reflection

        C# 和 Java 等语言已内置此功能,因此可能值得重新考虑您选择的语言,具体取决于您需要此功能。

        【讨论】:

          【解决方案7】:

          对于直接的 C++,上面的答案很好。但是,一些工具包(例如,Qt)可以为 C++ 添加自省功能。这可能是一个较重的更改,而不是您想要的现有项目,但是如果您正在启动一个可能需要自省的项目,那么值得搜索添加它的工具包/包装器。

          【讨论】:

            【解决方案8】:

            顺便提一下,存在一些反射库,例如 CAMP:http://dev.tegesoft.com/projects/camp/apidoc/index.html

            【讨论】:

              【解决方案9】:

              如果没有 RTTI 或某种地图,您将无法做到这一点。或者这样的解决方案:

              类Foo { 民众: 无效运行(字符串方法){ 酒吧(方法); foo2(方法); // ... 更多方法在这里 } 无效栏(字符串方法){ 如果(方法!=“酒吧”)返回; printf("功能栏\n"); } void foo2(字符串方法){ 如果(方法!=“foo2”)返回; printf("函数 foo2\n"); } } 富富; 诠释主要(无效){ foo.run('bar'); foo.run('foo2'); }

              这会给你想要的结果

              【讨论】:

              • 您建议的解决方案简直太糟糕了。这是低效的,因为对于 n 个方法,每次调用 run 无论如何都会导致 n 个方法调用。它也是不可维护的,因为将名称映射到方法的逻辑分布在方法之间。
              猜你喜欢
              • 1970-01-01
              • 2011-01-16
              • 1970-01-01
              • 2022-01-24
              • 1970-01-01
              • 2011-07-18
              • 1970-01-01
              • 2016-07-20
              • 1970-01-01
              相关资源
              最近更新 更多