映射类的函数

时间:2009-11-23 23:32:14

标签: c++ class function map macros

在我尝试映射我的类和命名空间之前,通过使用静态调用我成功了,现在我需要映射我的类的函数,因为它们将被动态使用。

首先,我想在构造函数中进行硬编码,这样我就可以使用指向函数本身的函数名称字符串来分配std:map。

例如:

class A{
  int B(){
      return 1;
  }
};

int main(){
  A *a = new A();
  vector<string, int (*)()> vec;

  vector["A.B"] = a.B;
}

我已经在A类上映射了函数B,我知道我只映射了该实例的函数,并且那个B不是静态的全局映射。

但那就是我需要的东西,在某些时候有人会给我一个字符串,我必须调用一个类实例的正确函数。

我的问题是,我是否只能通过在构造函数中进行硬编码来实现,因为这是我们正在讨论的实例范围,或者如果在函数声明中有某种方法可以做到这一点,就像这里的命名空间和类一样:

Somehow register my classes in a list

5 个答案:

答案 0 :(得分:3)

如果我理解正确,您希望地图存储一个指针,该指针可用于调用实例上的成员函数,该值在运行时从地图中选择。我将假设这是正确的事情,而且没有更简单的方法来解决同样的问题。很多时候,当你最终遇到奇怪的C ++回水时,这表明你需要再次看看你认为有的问题,看看这是否是解决问题的唯一方法。

使用普通函数指针的问题是非静态成员函数不是普通函数。假设您可以使用普通函数指针指向成员函数,当您取消引用该指针并调用该函数时会发生什么?成员函数需要一个对象来操作,并且语法不提供传递此对象的方法。

你需要一个指向成员的指针,这是一个稍微模糊的功能,语法相对棘手。当普通指针抽象对象时,指向成员的指针抽象出类上的成员;指针指定应该调用哪个类成员,但不指定从哪个对象获取成员(将在使用指针时指定)。我们可以使用这样的东西:

class B;

class A
{
    B some_function()
    { /* ... */ }
};


B (A::* myval)() = A::some_function;

这里myval是一个变量,表示A类的一个成员,在这种情况下成员some_function(尽管它可以指向同一类型的A的任何其他成员)。我们可以在任何我们想要的地方传递myval(例如将它存储在STL容器中,如在你的例子中),然后当我们想要调用该函数时,我们指定它应该被调用的实例以便找到函数:

A some_a;

B newly_created_b = (some_a.*myval)();

这适用于特定情况,但它不能解决您的一般问题,因为成员指针包含它们作为定义的一部分引用的。也就是说,以下两个变量的类型完全不同:

B (Foo::* first_variable)() = Foo::some_function;
B (Bar::* second_variable)() = Bar::some_function;

尽管两个函数在没有参数的情况下调用时都可以生成B,但这两个值对不同的类进行操作,因此您无法将一种类型的值分配给另一种类型的变量。这当然排除了将这些不同类型存储在单个STL容器中的作用。

如果您致力于将这些存储在容器中,则必须使用像Charles Salvia建议的基于仿函数的解决方案。

答案 1 :(得分:2)

如果我理解正确,你将会有一个类:

struct Foo
{
   int bar();
};

并且用户将输入类似“Foo :: bar”的字符串,并且从该字符串中您需要调用成员函数Foo :: bar?

如果是这样,由于静态类型系统,在C ++中编写灵活的解决方案相当尴尬。你可以使用一个std::map,其中键是一个字符串,值是一个成员函数指针,(或std::mem_fun_t对象),但这只适用于一个类,只适用于成员函数具有相同的签名。

您可以执行以下操作:

#include <iostream>
#include <map>
#include <functional>

struct Foo
{
 int bar() { std::cout << "Called Foo::bar!" << std::endl; }
};

int main()
{
 std::map<std::string, std::mem_fun_t<int, Foo> > m;
 m.insert(std::make_pair("Foo::bar", std::mem_fun(&Foo::bar)));

 Foo f;
 std::map<std::string, std::mem_fun_t<int, Foo> >::iterator it = m.find("Foo::bar");
 std::mem_fun_t<int, Foo> mf = it->second;
 mf(&f); // calls Foo::bar
}

答案 2 :(得分:1)

刚刚找到(使用谷歌)一个话题来回答与答案相同的问题。

What is the simplest way to create and call dynamically a class method in C++?

我还没有尝试但是有道理,稍后我会再问一下它是否有效

TY!

答案 3 :(得分:0)

  

我必须调用类实例的正确函数。

您需要在现有实例上调用特定方法,还是需要创建相应类型的实例并调用该方法?

如果是前者,则需要std::map或类似内容,以便从名称中查找实例。

如果是后者,那么基本上序列化框架需要做的是在反序列化时创建正确类型的对象,该对象知道如何读取下一位数据。您可以查看Boost序列化库如何处理它:

boost.org/doc/libs/1_40_0/libs/serialization/doc/serialization.html

答案 4 :(得分:0)

你是否在某种紧凑的循环中这样做,你需要一个好地图的效率?如果是这样,那么成员函数指针(当你链接到上面)是一个很好的方法。 (至少在你解决问题之后@Tim提到了将成员函数指针保存到同一个集合中的不同类型...让语言滥用开始!)

另一方面,如果这是在用户驱动的代码中,那么完全不酷并且写下来可能更容易辨认:

if( funcName=="A.b" )
{
    A a;
    a.b();
} else
// etc etc etc

对于性能较高的情况,您可以使用解析步骤和一些整数常量(或枚举)来补充相同的方法并使用开关。根据您的编译器,您实际上可能会比使用映射中的成员函数指针更好的性能:

switch( parse(funcName) )
{
    case A_b:
    {
       A a;
       a.b();
    }
    break;
}

(当然,如果你想从不同的地方填充你的可能性列表,这会分解......例如,如果每个类都要在启动期间注册自己。但是如果你有那种对象基础设施那么你应该是首先使用接口而不是指针!)