samedi 27 août 2016

Implementing signals (Observer pattern): is mutable or const_cast necessary?

I'm implementing my very own signal/slot (observer pattern, Qt style) mechanism so I can have a property that notifies ... stuff... that is has changed.

I think C++11 provides everything necessary to make a very succint and featureful implementation possible. The "issue" I'm running into is if I want to "connect" to a signal of a const object, I need the signal::connect function to be const, but modify the list of callbacks/observers. There are two straightforward ways to fix this:

  1. const_cast the lists inside connect.
  2. Make the lists mutable.

Both seem to me like the same thing (and this has been asked before, e.g. in this question), and perfectly fine logically, but stylistically questionable. Hence the question. Is there a way around this or is this a truly justified use of const_cast/mutable?

Some prelimenary code as I have it now:

template<typename... ArgTypes>
class signal
{
public:
  template<typename Callable>
  void connect(Callable&& callback) const
  {
    std::lock_guard<std::mutex> lock(slots_mutex);
    slots.emplace_back(callback);
  }

  void emit(ArgTypes... arguments) const
  {
    std::lock_guard<std::mutex> lock(slots_mutex);
    for(auto&& callback : slots)
    {
      callback(arguments...);
    }
  }

private:
  // mutable here allows to connect to a const object's signals
  mutable std::vector<std::function<void(ArgTypes...)>> slots;
  std::mutex slots_mutex;

};

Note I haven't tested this code; this is just a reflection of my current state of mind.

Aucun commentaire:

Enregistrer un commentaire