给定一个Event
结构和一个实现具有特定名称和原型的函数的对象,由Event
结构知道,我想返回一个指针或绑定到该函数。究竟它返回的并不重要;它可以很容易地成为指向成员的函数或绑定。
这有点难以解释,所以这里有一些伪代码:
struct Foo {
void onEvent();
};
struct Bar {
void onEvent();
};
struct Event
{
// I'm not sure what would go here
// Needs something that can be used to resolve T::onEvent, without
// knowing what T is until GetEventFunction is called.
typedef std::function<void()> function_type;
};
template<typename T, typename EventType>
EventType::function_type GetEventFunction(T* object)
{
return std::bind(T::(EventType::Something), object);
}
GetEventFunction<Foo, Event>(new Foo); // Returns Foo::onEvent
GetEventFunction<Bar, Event>(new Bar); // Returns Bar::onEvent
是否可以实现这种行为,或者C ++是否过于限制而不允许这样做?
请在回答之前阅读
我不是在寻找反思。据我所知,在编译时可以获得完成我目标所需的所有信息。
另外,我对替代方法不感兴趣。我知道很多方法可以通过其他代码实现此行为,例如每种事件类型的模板特化,但我正在寻找一种方法来实现这一目标。
答案 0 :(得分:4)
也许我没有解释清楚,但每个
Event
类型的函数名称都是唯一的。FooEvent
应解决T::onFooEvent
,而BarEvent
应解决T::onBarEvent
。
C ++可以对类型和值进行操作,但不能对名称进行操作。这是处理文本,这是一个宏观层面的事情,发生在C ++正确看到代码之前。您不能使用BarEvent
类型并将其转换为函数T::onBarEvent
,因为它们之间没有关联,除了它们恰好是命名为。
这就是Luc的答案使用特定名称的原因:函数名称必须是硬编码的。
现在,您可以通过使用特征模板来略微改变C ++的规则。例如,您可以创建一个event_traits
模板类型,该模板类型具有成员函数,该函数需要T
并在其上调用特定函数。它看起来像这样:
template<typename event_type>
struct event_traits
{
template<typename T> void Dispatch(T *t) {t->DefaultEventFunction();}
};
以上使用DefaultEventFunction
。
如果您希望每个Event
都有自己的事件函数,则需要为每个Event
类提供专门化。如果您想强制执行此规则,则永远不要在任何DefaultEventFunction
个对象中定义T
;编译器会抱怨。将名称更改为不太可能使用的名称,例如WhyDidYouNameThisFunctionLikeThisStopIt
。
template<>
struct event_traits<FooEvent>
{
template<typename T> void Dispatch(T *t) {t->onFooEvent();}
};
template<>
struct event_traits<BarEvent>
{
template<typename T> void Dispatch(T *t) {t->onBarEvent();}
};
这是宏可以派上用场的地方:
#define REGISTER_EVENT_HANDLER(eventName)\
template<> struct event_traits<eventName>\
{\
template<typename T> void Dispatch(T *t) {t->on ## eventName ();}\
};
因此,您的GetEventFunction
将如下所示:
template<typename T, typename EventType>
EventType::function_type GetEventFunction(T* object)
{
return std::bind(event_traits<EventType>::Dispatch<T>, object);
}
答案 1 :(得分:2)
如果你有成员的名称,那么你不需要知道一个类型 - 假设该成员不是一个重载的成员函数。
template<typename T>
auto GetEventFunction(T& object)
-> decltype( std::bind(&T::onEvent, std::ref(object)) )
{ return std::bind(&T::onEvent, std::ref(object)); }
// Usage:
Foo f;
auto event = GetEventFunction(f);
请注意,这有点人为,因为您提到的onEvent
不带任何参数。如果是这样,你需要更多的脚手架。 (我建议写一个也接受一个对象的mem_fn
,与std::mem_fn
不同。)