我创建了一个用于在小型图书馆中调度和处理事件的类。我的目标是使它尽可能易于使用,避免宏观污染。事件是在编译时注册的,侦听器正在运行时中订阅事件。两者均由Resource
命名空间中的枚举标识。 Pack
存储可变参数模板参数。 EventInfo
是一种告诉我们将在Listener
和Event
中使用哪些参数的方法。代码:
namespace Resource
{
enum class Resource::EventId;
enum class Resource::ListenerId;
}
template< class EventId, EventId id >
struct EventInfo;
template< class... Args >
struct Pack
{
};
template< class ListenerId_, class... EventData >
struct Listener
{
using ListenerId = ListenerId_;
ListenerId Id = ListenerId::Undefined;
bool IsEnabled = false;
std::function< void( EventData... ) > Callback = nullptr;
};
template< class ListenerId_, class... EventData >
struct EventInfoHelper
{
using Data = Pack< EventData... >;
using Listener = typename Listener< ListenerId_, EventData... >;
using ListenerId = typename Listener::ListenerId;
};
#define REGISTER_EVENT_W_ARGS( NAME, ... ) \
template<> \
struct EventInfo< Resource::EventId, Resource::EventId::NAME > \
{ \
public: \
using Hlpr = EventInfoHelper< Resource::ListenerId, __VA_ARGS__ >; \
using Data = Hlpr::Data; \
using Listener = Hlpr::Listener; \
using ListenerId = Hlpr::ListenerId; \
};
REGISTER_EVENT_W_ARGS( OnUpdate, const sf::Time& );
// ... macro undef here
template< class Id, Id id >
class Event
{
public:
using Data = typename EventInfo< Id, id >::Data;
using Listener = typename EventInfo< Id, id >::Listener;
using ListenerId = typename EventInfo< Id, id >::ListenerId;
template< class... DataUnpacked, class = std::enable_if_t< std::is_same< Pack< DataUnpacked... >, Data >::value > >
static void Dispatch( DataUnpacked... data )
{
for( auto& listener : m_listeners )
{
CHECK( listener.Id != ListenerId::Undefined );
if( listener.IsEnabled )
{
CHECK( listener.Callback != nullptr );
listener.Callback( data... );
}
}
}
// ...
private:
static std::vector< Listener > m_listeners;
};
当前用例:
Event< Resource::EventId, Resource::EventId::OnUpdate >::Dispatch< const sf::Time& >( dt );
所需(从EventInfo::Data
推导出的参数):
Event< Resource::EventId, Resource::EventId::OnUpdate >::Dispatch( dt );
问题是如何使用在Dispatch
中注册为EventInfo::Data
参数的参数来实现Pack< Args... >
?
答案 0 :(得分:1)
您提供的代码不完整,因此我无法在您的示例中集成该解决方案。
您可以使用基类从Pack <...>中提取参数包,并在基类的函数中将其展开
template<typename ...>
class Pack {};
template<typename>
class test_base;
template<typename ... Args>
class test_base<Pack<Args...>> {
public:
void Dispatcher(Args... args) {
//implemntation
}
};
template<typename ... Args>
class test : public test_base<Pack<Args...>> {
};
答案 1 :(得分:0)
不确定要理解(由于您的示例太不完整,我可以尝试编译),但在我看来,您可以通过附加的typename模板参数(带有默认值和特殊化)来传递。
我的意思是...类似
template <typename Id, Id id, typename = typename EventInfo<Id, id>::Data>
class Event;
template <typename Id, Id id, typename ... DataUnpacked>
class Event<Id, id, Pack<DataUnpacked...>>
{
// ...
public:
static void Dispatch (DataUnpacked ... data)
{
// ...
}
};
这样,当您创建变量Event<EventId, EventId::someId>
时,对于某些Event<EventId, EventId::someId, typename EventInfo<EventId, EventId::someId>::Data>
可变参数列表,它的默认值为Event<EventId, EventId::someId, Pack<Ts...>>
的{{1}}。
专业化许可可以拦截Ts...
列表(按照您的示例,为Ts...
),并在DataUnpacked...
签名中使用它。
非主题建议:如aschepler所观察到的,您标记了C ++ 17,因此可以使用Dispatch()
模板值;所以代码可以很简单
auto
如果template <auto id, typename = typename EventInfo<id>::Data>
class Event;
template <auto id, typename ... DataUnpacked>
class Event<id, Pack<DataUnpacked...>>
{
// ...
public:
static void Dispatch (DataUnpacked ... data)
{
// ...
}
};
也收到EventInfo
。