Tremah
Tremah

Reputation: 105

Variadic template inheritance order and syntax

I am trying to implement a signal/slot system via templates to get into some advanced usage of templates. I looked up existing implementations and found one at https://github.com/skypjack/eventpp. I have been trying to understand how it works and pretty much figured it out but there are still some things I can't wrap my head around and couldn't find anything on the internet (didn't know what to search for).

Following is an executable minimal example of the code I took from the github repo plus additional code I implemented myself:

//Signal implementation (own code)
template <typename S>
struct Signal
{
  using type = S;
};

struct KeyPressedEvent : Signal<KeyPressedEvent>
{
  KeyPressedEvent(int keyCode) : keyCode_{keyCode} {}
  int keyCode() {return keyCode_;}

  private:
    int keyCode_;
};

struct KeyReleasedEvent : Signal<KeyReleasedEvent>
{
  KeyReleasedEvent(int keyCode) : keyCode_{keyCode} {}
  int keyCode() {return keyCode_;}

  private:
    int keyCode_;
};    

//Signal Bus implementation (github repo code)
template<int S, typename... E>
struct BusBase;

template<int S, typename E, typename... O>
struct BusBase<S, E, O...> : BusBase<S, O...>
{
  BusBase()
  {
    if(std::is_same<E, KeyPressedEvent>::value)
    {
      std::cout << "BusBase<S, E, O...> KeyPressedEvent" << std::endl;
    }
    if(std::is_same<E, KeyReleasedEvent>::value)
    {
      std::cout << "BusBase<S, E, O...> KeyReleasedEvent" << std::endl;
    }
  }

  Signal<E> signal_;
};

template<int S>
struct BusBase<S>
{
  BusBase()
  {
    std::cout << "BusBase<S>" << std::endl;
  }
};

template <typename... E>
struct Bus : BusBase<sizeof...(E), E...>
{
  Bus()
  {
    std::cout << "Bus" << std::endl;
  }
};

int main(int& argc, const char* args[])
{
  Bus<KeyPressedEvent, KeyReleasedEvent> eventBus;

  std::cin.get();
}

Here is the output I get when I run the program:

BusBase<S>
BusBase<S, E, O...> KeyReleasedEvent
BusBase<S, E, O...> KeyPressedEvent
Bus

I will state what I understood from the implementation. Please correct me if I am wrong.

template<int S, typename... E>
struct BusBase;

is needed to expand the parameter pack from Bus

BusBase<S, E, O...>

is instantiated for every Signal in Bus

BusBase<S>

is needed when the parameter pack has been fully expanded

My questions:

1.

BusBase<S>

Why is this template instantiated first? I would have expected it to be last (before Bus).

2.

template<int S>
struct BusBase<S>

template<int S, typename E, typename... O>
struct BusBase<S, E, O...> : BusBase<S, O...>

Why are the parameters both in the template declarations and in the struct declarations?

Thank you for your help.

Upvotes: 1

Views: 113

Answers (1)

Jarod42
Jarod42

Reputation: 217870

Why is this template instantiated first? I would have expected it to be last (before Bus).

constructor call and instantiation are different thing

Bus<KeyPressedEvent, KeyReleasedEvent> inherits from BusBase<2, KeyPressedEvent, KeyReleasedEvent> which inherits from BusBase<2, KeyReleasedEvent> which inherits from BusBase<2>

Call order is from (grand-)parents, members, constructor block, so the outputs you have.

Why are the parameters both in the template declarations and in the struct declarations?

That is the syntax for partial template specialization.

As a note partial specialization is unneeded here and can be rewritten as:

template<int S, typename ... Es>
struct BusBase
{
  BusBase()
  {
     std::cout << "BusBase<S>\n";
     (print<Es>(), ...);

  }

  template <typename T>
  void print() const
  {
      if constexpr (std::is_same<T, KeyPressedEvent>::value) {
          std::cout << "BusBase<S, Es...> KeyPressedEvent" << std::endl;
      } else if constexpr (std::is_same<T, KeyReleasedEvent>::value) {
          std::cout << "BusBase<S, Es...> KeyReleasedEvent" << std::endl;
      }
  }

  std::tuple<Signal<Es>...> signals_;
};

Upvotes: 3

Related Questions