存储和调用具有不同签名的成员函数的unordered_map

时间:2017-03-15 06:20:50

标签: c++ templates dynamic

我正在创建一个名为MessageableObject的类,它可以在地图中注册自己的成员函数。可以通过调用sendMessage从外部调用这些函数,提供返回类型作为模板参数,并将函数名称以及参数作为可变参数传递。我之前看到过与此类似的东西,只有函数不是存储它们的类的成员函数。

以下是我到目前为止编写的代码,以及应该成功的测试用例。

#include <iostream>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>

class MessageableObject
{
    using _message_ptr = void(MessageableObject::*)(void);

    std::unordered_map<const char*, std::pair<_message_ptr, std::type_index>> _message_map;

public:

    MessageableObject()
    {
    }

    template<typename F>
    void addMessage(const char* name, F func)
    {
        auto type = std::type_index(typeid(func));
        _message_map.insert(std::make_pair(name, std::make_pair((_message_ptr)func, type)));
    }

    template<typename R, typename... Args>
    R sendMessage(const char* name, Args&&... args)
    {
        auto iter = _message_map.find(name);
        assert(iter != _message_map.end());
        auto type = iter->second;
        auto func = reinterpret_cast<R(MessageableObject::*)(Args ...)>(type.first);
        assert(type.second == std::type_index(typeid(func)));
        return func(std::forward<Args>(args)...);
    }

    void foo()
    {
        std::cout << __FUNCTION__ << std::endl;
    }

    int bar(int i, int j)
    {
        std::cout << __FUNCTION__ << ' ' << i + j << std::endl;
        return i + j;
    }
};

int main()
{
    MessageableObject obj;
    obj.addMessage("foo", &MessageableObject::foo);
    obj.addMessage("bar", &MessageableObject::bar);

    obj.sendMessage<void>("foo");
    int baz = obj.sendMessage<int>("bar", 1, 2);

    return 0;
}

目前,此代码生成以下错误两次:

  

C2064:术语不评估为采用 n 参数的函数

第一次当我尝试呼叫foo时n为0,而当我尝试呼叫bar时n为2。为什么会出现这些错误?我甚至可以使用当前语法实现这一点吗?

作为参考,可以在此处找到适用于非成员函数的此实现的版本:https://stackoverflow.com/a/33837343/962805

非常感谢任何和所有帮助。我对类型擦除和成员函数绑定知之甚少。这样做的目的是创建动态消息传递统一引擎的GameObject.sendMessage功能。

更新:根据skypjack的回答,如果消息调用包含变量,上述解决方案将会中断。可以通过将sendMessage中的Args&&...更改为Args...来解决此问题,但这会带来一个新问题:如果将消息添加到包含引用参数的地图(例如int bar(int&, int)),它无法被隐式调用。调用sendMessage<int, int&, int>("bar", x, 2)将按预期运行。

1 个答案:

答案 0 :(得分:2)

如果您在文件顶部包含<cassert>并使用此文件:

return (this->*func)(std::forward<Args>(args)...);

而不是:

return func(std::forward<Args>(args)...);

它编译。<​​/ p>

我不能说它适用于它看起来我很幸运避免使用UB并且它显然有效(这是一个非常有效的UB)会更合适。
问题是bar有类型:

int(MessageableObject::*)(int i, int j);

你把它演绎成:

void(MessageableObject::*)(void);

然后你将它强制转换为它正确的类型,因为你将其调用为:

obj.sendMessage<int>("bar", 1, 2);

如果您这样做了:

int x = 1;
obj.sendMessage<int>("bar", x, 2);

您可以执行演员:

int (MessageableObject::*)(int &i, int j);

错误的返回类型会发生类似情况。所有这些都是UB。因此,从我的角度来看,在生产环境中使用它的解决方案太弱了。