繁体   English   中英

将可变参数模板参数包解压缩为函数签名

[英]Unpack variadic template parameter pack into function signature

我已经创建了用于在一个小型库中调度和处理事件的类。 我的目标是使它尽可能易于使用,避免宏观污染。 事件是在编译时注册的,侦听器正在运行时中订阅事件。 两者均由Resource名称空间中的枚举标识。 Pack是用于存储可变参数模板参数的。 EventInfo是一种方法,用于告知我们将在ListenerEvent使用哪些参数。 码:

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 );

问题是如何使用在EventInfo::Data作为Pack< Args... >参数注册的参数来实现Dispatch

您提供的代码不完整,所以我无法在您的示例中集成该解决方案。

您可以使用基类从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...>> {
};

不确定理解(我可以尝试编译,因为您的示例太不完整了),但是在我看来,您可以通过附加的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> ,它默认为Event<EventId, EventId::someId, typename EventInfo<EventId, EventId::someId>::Data> ,即Event<EventId, EventId::someId, Pack<Ts...>>用于某些Ts...可变参数列表。

专业化许可可拦截该Ts...列表(如您的示例所示,为DataUnpacked... )并在Dispatch()签名中使用它。

非主题建议:如aschepler所观察到的,您标记了C ++ 17,因此可以使用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也收到一个auto id

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM