У меня есть этот класс:
#include <assert.h>
template <typename DeclaringType, typename HandlerType>
using SubscribeMethodType = void (DeclaringType::*)(HandlerType* handler);
template <typename DeclaringType>
using UnsubscribeMethodType = void (DeclaringType::*)();
template <SubscribeMethodType<???_1, ???_2> subscribeMethod, UnsubscribeMethodType<???_1> unsubscribeMethod>
class EventHandler
{
private:
template <typename T>
struct ExtractDeclaringAndHandlerTypes
{
};
template <typename DeclaringType, typename HandlerType>
struct ExtractDeclaringAndHandlerTypes<void (DeclaringType::*)(HandlerType*)>
{
typedef DeclaringType DeclaringType;
typedef HandlerType HandlerType;
};
typedef typename ExtractDeclaringAndHandlerTypes<decltype(subscribeMethod)>::DeclaringType DeclaringType;
typedef typename ExtractDeclaringAndHandlerTypes<decltype(subscribeMethod)>::HandlerType HandlerType;
public:
EventHandler() { }
~EventHandler()
{
Unsubscribe();
}
void Subscribe(DeclaringType* eventOwner, HandlerType* handler)
{
assert(!m_IsSubscribed);
m_EventOwner = eventOwner;
(eventOwner->*subscribeMethod)(handler);
m_IsSubscribed = true;
}
void Unsubscribe()
{
if (m_IsSubscribed)
{
(m_EventOwner->*unsubscribeMethod)();
m_IsSubscribed = false;
}
}
private:
DeclaringType* m_EventOwner;
bool m_IsSubscribed;
};
Пример использования:
class IEventConsumer
{
};
class ExampleEventOwner
{
public:
void Subscribe(IEventConsumer* consumer) {}
void Unsubscribe() {}
};
int main()
{
ExampleEventOwner owner;
IEventConsumer consumer;
EventHandler<&ExampleEventOwner::Subscribe, &ExampleEventOwner::Unsubscribe> handler;
handler.Subscribe(&owner, &consumer);
handler.Unsubscribe();
return 0;
}
Можно ли указать что-то вместо «??? _ 1» и «??? _ 2», чтобы они могли быть любого типа? Цель состоит в том, чтобы избежать того, чтобы потребителю этого класса приходилось явно указывать DeclaringType и HandlerType, так как они могут быть легко выведены из subscribeMethod и unsubscribeMethod.
Примечание: я не могу использовать C ++ 17.
Можно ли указать что-то вместо «??? _ 1» и «??? _ 2», чтобы они могли быть любого типа?
Не раньше C ++ 17, нет. То, что вы запрашиваете, — это параметр не типового шаблона, который может определить его тип из предоставленного значения — именно это и является новой функцией языка C ++ 17. template auto
делает.
До C ++ 17 единственный способ сделать это — сначала предоставить тип:
template <class T, T Value> struct X;
Итак, вы должны написать:
EventHandler<decltype(&ExampleEventOwner::Subscribe), &ExampleEventOwner::Subscribe,
decltype(&ExampleEventOwner::Unsubscribe), &ExampleEventOwner::Unsubscribe
> handler;
который, по общему признанию, отстой. Кроме того, вы можете изменить дизайн, чтобы параметры шаблона соответствовали типу класса и типу обработчика. Это позволит вам написать:
EventHandler<ExampleEventOwner, IEventConsumer> handler(
&ExampleEventOwner::Subscribe, &ExampleEventOwner::Unsubscribe);
что в свою очередь позволит вам написать фабричную функцию:
template <class T, class H>
EventHandler<void (T::*)(H*), void(T::*)()> makeEventHandler(
void (T::*subscribe)(H*), void (T::*unsubscribe)());
auto handler = makeEventHandler(&ExampleEventOwner::Subscribe,
&ExampleEventOwner::Unsubscribe);
И это определенно не страшно.
Других решений пока нет …