Home > Enterprise >  How can I infer a template parameter from an internal alias?
How can I infer a template parameter from an internal alias?

Time:04-22

I have a template class with two parameters:

template<class TEvent, class TData>
class EventPool {
public:
  using EventObserver = std::function<void(const TData &)>;
  using EventData = TData;
  using EventType = TEvent;

public:
  EventObserverId observe(TEvent event, EventObserver &&observer) { ... }
  void deleteObserver(EventObserverId observerId) { ... }
  void dispatch(TEvent event, const TData &data) { ... }
};

I also have another container class that stores many pools:

class EventSystem {
  using CollisionPool = EventPool<CollisionEvent, CollisionData>;
  using MouseButtonPool = EventPool<MouseButtonEvent, MouseButtonData>;
  using KeyboardPool = EventPool<KeyboardButtonEvent, KeyboardButtonData>;
public:


private:
  std::tuple<CollisionPool, MouseButtonPool, KeyboardPool> mPools;
};

Now, I want to create a template based function that automatically infers the correct pool and calls the functions inside the pool. For example:

template<class TEvent>
EventObserverId observe(TEvent event, DeriveThePoolHere::EventObserver &&observer) {
  auto &pool = getPoolByFromEventType<TEvent>();
  return pool.observe(event, observer);
}

template<class TEvent>
void deleteObserver(TEvent event, EventObserverId observerId) {
  auto &pool = getPoolFromEevntType<TEvent>();
  pool.deleteObserver(observerId);
}

void dispatch(TEvent event, const DeriveThePoolHere::EventData &data) {
  auto &pool = getPoolFromEventType<TEvent>();
  pool.dispatch(event, data);
}

If I can somehow create a type map that maps an event to the event data, I can solve this problem but I am not sure how to do this in C .

CodePudding user response:

With help of if constexpr and std::apply (c 17 needed)
Maybe like this:

template<typename TPool, typename TEvent, typename TData>
void dispatchIfMatch(TPool& pool, TEvent event, const TData& data) {
    if constexpr(std::is_same<TEvent, typename TPool::EventType>::value) {
        pool.dispatch(event, data);
    }
}

template<typename TEvent, typename TData>
void dispatch(EventSystem& esys, TEvent event, const TData& data) {
    std::apply(
        [&](auto&&... args) {
            ((dispatchIfMatch(args, event, data)), ...);
        }, esys.mPools);
}

test it here: http://coliru.stacked-crooked.com/a/2c2231c860d8023c

CodePudding user response:

I was able to solve this issue using SFINAE (is that what it is called?). I created an empty struct and specialized it with different events:

namespace detail {

template<class TEvent>
struct GetEventData {};

template<>
struct GetEventData<CollisionEvent> {
  using Data = CollisionData;
};

template<>
struct GetEventData<MouseButtonEvent> {
  using Data = MouseButtonData;
};

}

In order to make my life easier, I created another alias inside my class (note that this type of specialization cannot be inside the class):

class EventSystem {
  template<class TEvent>
  using GetEventPool = EventPool<TEvent, detail::GetEventData<TEvent>;

public:
  // explained later

private:
  std::tuple<GetEventPool<CollisionEvent>, GetEventPool<MouseButtonEvent>> mPools;
};

Since GetEventPool is a type alias, I have type aliases inside EventPool, and tuple item can be retrieved by type, I was able to combine these three things together:

template<class TEvent>
EventObserverId observe(TEvent event, typename GetEventPool<TEvent>::EventObserver &&observer) {
  using CurrentPool = GetEventPool<TEvent>;
  return std::get<CurrentPool>(mPools).observe(event, observer);
}

template<class TEvent>
void deleteObserver(TEvent event, typename EventObserverId observerId) {
  using CurrentPool = GetEventPool<TEvent>;
  std::get<CurrentPool>(mPools).deleteObserver(observerId);
}

template<class TEvent>
void dispatch(TEvent event, const typename GetEventPool<TEvent>::EventData &data) {
  using CurrentPool = GetEventPool<TEvent>;
  std::get<CurrentPool>(mPools).dispatch(event, data);
}

This works very well and also shows compiler error if incorrect event or event type is being passed. There is going to be some redundancy in terms of data types; so, one thing that I am still trying to do is simplify the tuple somehow:

PoolTuple<CollisionEvent, MouseButtonEvent, ...> mPools;
  • Related