1

给定一个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++ 过于受限而不允许这样做?

请在回答前阅读此内容

我不是在寻找反思。据我所知,完成我的目标所需的所有信息都在编译时可用。

另外,我对替代方法不感兴趣。我知道通过附加代码实现此行为的许多方法,例如每种事件类型的模板特化,但我正在寻找一种专门实现此目的的方法。

4

2 回答 2

4

可能我没有解释好,但是函数名对于每种Event类型都是唯一的。AFooEvent应该解决T::onFooEvent,而 aBarEvent应该解决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);
}
于 2012-08-10T19:18:06.883 回答
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。)

于 2012-08-10T18:46:42.927 回答