我正在创建一个名为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)
将按预期运行。
答案 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。因此,从我的角度来看,在生产环境中使用它的解决方案太弱了。