我怎样才能通过方法名称来调用该方法?

5

我正在尝试创建一个方法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

谢谢! :)

1
你想要实现什么目标? - just somebody
9个回答

15

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

该映射将被声明为:

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

2
否则,if(){}else if(){}else if(){} else{} 还在这里;-) - Michael Krelin - hacker
2
如果我选择这个解决方案,我会将其作为静态映射 - 永远不要把可以在编译时完成的事情推迟到运行时。 - Michael Krelin - hacker
一旦创建了该映射,您如何填充并调用它? - Will Tatam
这对非静态方法也适用吗? - Will Tatam

9
正如其他人所指出的,C++没有开箱即用的反射功能(很可能这不是你想做的事情)。
但我要提到,确实存在一些预处理器可以为某些类类型和方法实现这种功能。Qt的moc就是其中一个例子,它是信号/槽机制的一部分。请注意QMetaObject上的method()methodCount()...
Qt的实现方式是在构建过程中注入一个工具,制作表格并将其与代码一起编译。这些都是手写的内容--而非语言特性。

6

之前提到的地图解决方案可以用于固定函数列表,但我认为在C++中一般情况下没有办法像这样进行内省。也就是说,你不能有一个函数void perform( string methodName )来判断是否存在名为methodName的方法,如果存在,则调用它。


是的,在这方面,C ++和Java完全不同。 - Omnifarious
@benzado:Objective-C完全不同,你发送一条消息。如果对象知道如何响应该消息,它将执行代码。Python/Perl/Javascript:所有对象都只是映射,因此方法只是作为值附加的对象。C#和Java不同之处在于允许类型内省(又称反射),从而允许您按名称查找方法,然后调用它们。 - Martin York
2
@benzado:继续使用地图作为所有对象的基础会使类型安全成为一场噩梦。Java/C#反射对于开发工具构建者(和一些库)来说非常棒。但在实际应用中使用反射会导致脆弱的代码。 - Martin York
@MartinYork: 如果你说 Objective-C 没有提供运行时内省,那么你是错误的(请查看 class_copyMethodList() 和相关函数)。如果你想挑起强类型系统和弱类型系统之争,我不感兴趣。否则,我不确定你想表达什么意思。 - benzado
@benzado,我不喜欢Java,但是无论好坏,这是现在学校教授的内容,所以我认为原帖作者最熟悉它。 - Omnifarious
@benzado:我从未说过Objective-C没有内省,但你也不需要使用它来按名称调用方法,因为Objective-C在对象上运行时绑定消息。我并不是想挑起争端,只是想在你简洁的评论中提供更多详细信息。 - Martin York

3
虽然之前的答案可以解决您的问题,但是看起来不是最正确的解决方案。我有两个建议: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");
}

总之,请仔细思考您的设计,看看是否有更好的流程不需要函数名称,而是让函数决定执行或者通用地执行盲目的方法。


2

1

就像其他帖子中所说的那样,你需要在 C++ 中手动完成这个操作,因为该语言没有内置 反射 功能。

C# 和 Java 等语言内置了这个功能,因此根据您所需的情况重新考虑您选择的编程语言可能会更好。


1

对于纯C++,上面的答案是不错的。然而,一些工具包(例如Qt)可以为C++添加内省功能。这可能是一个较大的改变,并不适用于现有项目,但如果您正在启动一个可能需要内省的项目,那么值得寻找添加内省功能的工具包/包装器。


0

-4

你不能没有RTTI或某种映射解决方案来完成它。或者像这样的解决方案:

class Foo {
    public:
    void run( string method ) {
        bar(method);
        foo2(method);
     // ... 这里还有更多的方法
    }
    void bar(string method) {
        if (method != "bar") return;
        printf( "Function bar\n" );
    }
    void foo2(string method) {
        if (method != "foo2") return;
        printf( "Function foo2\n" );
    }
}
Foo foo;
int main( void ) { foo.run( 'bar' ); foo.run( 'foo2' ); }

这将给你想要的相同结果。


3
你的建议解决方案非常糟糕。它是低效的,因为无论如何,在调用run时,每个方法都要进行n次方法调用。而且它也难以维护,因为将名称映射到方法的逻辑分散在方法之间。 - benzado

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接