5

Background

I had come across and application where I had access to a method:

 void AttachCallback(int event, std::functional<void(int)> cb);

which allowed me to attach a callback cb to event event. I also had several classes that would need to attach callbacks to a couple events in their constructors and implement the callback for each event they bound to. So the first implementation that comes to mind looks like:

class MyClass {
 public:
  MyClass() {
    AttachCallback(0, [this](int value) { cb0(value); });
    AttachCallback(2, [this](int value) { cb2(value); });
    // ... as necessary
  }
 private:
  void cb0(int value) { /*... callback for event 0 ...*/ }
  void cb2(int value) { /*... callback for event 2 ...*/ }
  // ... as necessary
};

But, since I've been playing around with templates a lot recently, I wondered if I could create a templated pure-virtual class for listening to a given event and use it as follows:

template<const int EVENT>
class ListensToEvent {
 public:
  virtual ~ListensToEvent() = default;

 protected:
  ListensToEvent() {
    AttachCallback(EVENT, [this](int value) { cb(value); });
  }
  virtual void cb(int value) = 0;
};

class MyClass : public ListensToEvent<0>, public ListensToEvent<2> {
 private:
  void cb(int value) override;
};

template<>  // This line makes me suspicious
void MyClass::ListensToEvent<0>::cb(int value) {
  /*... callback for event 0 ...*/
}
template<>
void MyClass::ListensToEvent<2>::cb(int value) {
  /*... callback for event 2 ...*/
}

When searching around for related topics, I found this thread which shows how to use helper classes to remedy clashes that occur when two base interfaces provide the same identifier pure-virtual method. Since I use templates here, I wouldn't be able to use helpers like that because I don't know how many instantiations there could be, but I found that declaring the method without specialization and then providing template specializations outside the class definition allowed me to target each base classes' method individually (as shown above).

Proof of Concept

While testing this out, I built a simplification of this design (without callbacks) just to show that it compiles and specializes here.

Question

Primarily, I'm concerned with my understanding of why this works. It makes sense to me that I would be able to specify which cb I'm implementing after the class definition by qualifying cb with either ListensToEvent<0> or ListensToEvent<2>. What I don't understand is why that qualification counts as a template specialization and therefore why the line template<> is needed. Additionally, if these qualifications are in fact template specializations, how exactly does C++ view the method specializations and why do they work?

With that said, I'm also interested in comments about the functionality of this design. Is it an efficient way to simplify MyClass and it's other siblings implementations, or would it have been better done the first way I proposed? Or is there another design that would work best in this situation?

Community
  • 1
  • 1
  • Why template the whole class and not just the method `cb`? I don't like it. – Stargateur Dec 14 '16 at 11:20
  • @Stargateur I see your point, but I needed the template argument `EVENT` in the constructor to attach the callback, as well as to specialize the method `cb`. I'm not sure how to implement `ListensToEvent` so that only the method is templated while still achieving the same effect. – define cindy const Dec 14 '16 at 11:23
  • 1
    You right, look [this](http://ideone.com/FKWGwA). You will see that you don't need call in Test. And call in special don't need to be virtual. It why it's a specialization of template. – Stargateur Dec 14 '16 at 11:36
  • 1
    The code which you say 'works' is incorrect. Just look at the warnings generated by clang http://coliru.stacked-crooked.com/a/91918cbf47bdb691 – Arunmu Dec 14 '16 at 11:42
  • Oh wow, that's not what I thought was happening at all! So when I write `template <> Test::Special<0>::call`, I'm not really specifying to use override `Special<0>`'s `call`, I'm actually specializing the method `call` for class `Special` where `EVENT = 0`! And I'm just doing this awkwardly through Test. So this would fail if I added a second class listening to the same event. – define cindy const Dec 14 '16 at 11:49
  • Based on all that, I'm actually surprised the compiler is not yelling at me for specializing the virtual method of `Special`..? – define cindy const Dec 14 '16 at 11:50
  • @definecindyconst with clang you need "-Wall". Can we have more detail with the library that you want to use? Maybe we can help you to find a better way. – Stargateur Dec 14 '16 at 11:58
  • @Stargateur You're right, I really should have my warnings on. Thanks for the offer on design there, but I was playing with the design a bit more and I need to be able to attach and detach the callbacks based on state. So at that point, having an interfaces for each event is purposeless as it doesn't actually simplify or reduce code. I think I'm gonna scrap this design for now, but I thought it was an interesting concept, glad to see why it doesn't work and understand templates a bit better! – define cindy const Dec 14 '16 at 12:02
  • 1
    Are you trying to do something similar to [this](http://melpon.org/wandbox/permlink/2yBmN65P9MSX6C8B)? – W.F. Dec 14 '16 at 12:04
  • 1
    @definecindyconst You _are_ allowed to specialize a virtual function of a template class. What compiler does not allow you is to create a template member function which is virtual ! The original warning reported by the compiler is that, you are calling `call` too early, by which time the Virtual table for dispatching is not setup correctly and cannot forward/dispatch the call to the concrete class object. http://coliru.stacked-crooked.com/a/24c9c5b68155092e This will give you the ambiguity error which you were expecting – Arunmu Dec 14 '16 at 12:06
  • @Arunmu I didn't know that, thanks! I'm gonna play around with this some more and look at the errors to try to better understand the whole virtual members of template classes concept. – define cindy const Dec 14 '16 at 12:10
  • @W.F. Wow that is some interesting code. I'm not sure at this minute (on my phone) if I can apply this to what I need and actually see effective code reduction and simplification (this might be a bit OP for my use case!). But it looks flexible enough to be applicable regardless. I'm definitely going to take a longer look at this design when I have a minute, I really like the clever use of a specialization of a member template function based on the class template. And then the use of a middle class with a variadic template to allow grouping of ListenToEvents is elegant! – define cindy const Dec 14 '16 at 12:15

0 Answers0