Распакуйте пакет параметров шаблона variadic в сигнатуру функции - PullRequest
0 голосов
/ 06 декабря 2018

Я создал класс для отправки и обработки событий в небольшой библиотеке.Моя цель состоит в том, чтобы сделать его максимально простым в использовании, избегая макро-загрязнения.События регистрируются во время компиляции, слушатели подписываются на события во время выполнения.Оба идентифицируются по перечислениям в 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... > аргументы?

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

Не уверен, что понимаю (и я могу попытаться скомпилировать, потому что ваш пример слишком неполный), но мне кажется, что вы можете пройти через дополнительный параметр шаблона 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...>> для некоторого 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.

0 голосов
/ 06 декабря 2018

код, который вы предоставили, был неполным, поэтому я не смог интегрировать решение в вашем примере.

вы можете использовать базовый класс, чтобы извлечь пакет параметров из пакета <...> и развернуть егов функции в базовом классе

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...>> {
};
...