#pragma once #include #include #include #include #include #include namespace AMDiS { namespace event { /** * An event that is signaled before the actual adaption happens. Example: grid.preAdapt(). * The \ref value might indicate whether any pre-processing is necessary. **/ struct preAdapt { bool value = true; }; /** * An event that is called directly of the adaption. Example: grid.adapt(). * The \ref value indicates whether something is changed during adaption. **/ struct adapt { bool value = true; }; /** * An event that is called after adaption to indicate the start of a clean-up phase. **/ struct postAdapt {}; } template class ObserverInterface { public: virtual ~ObserverInterface() = default; virtual void update(Event e) = 0; virtual void unset() = 0; }; /// \brief Mixin for signaling of certain events. /** * Derived classes T can emit a signal e by calling notify(e). This will send the signal to all * classes U... deriving from Observer if * - the type of the event is included in Events, * - T = S or S is an Observer of T (directly or indirectly via other Observers), * - U called the Observer constructor with an instance of S that has direct or indirect access * to the instance of T (see Observer) */ template class Notifier : public Notifier , public Notifier { public: using Notifier::notify; using Notifier::notify; }; template class Notifier { public: virtual ~Notifier() { // Remove remaining pointers to this to avoid segfaults for (ObserverInterface* o : observers_) o->unset(); } /// Call the \ref update method on all attached observers. void notify(Event const& e) { for (ObserverInterface* o : observers_) o->update(e); } /// Attach a new observer that gets called on \ref notify void attach(ObserverInterface* o) { observers_.insert(o); } /// Detaches the passed observer from the list, if stored. void detach(ObserverInterface* o) { observers_.erase(o); } private: std::set*> observers_; }; /// Implementation of the \ref ObserverInterface template class Observer : public ObserverInterface { public: template ,N>)> Observer(N const& notifier) : notifier_(const_cast(¬ifier)) { notifier_->attach(this); } template ,N>)> Observer(N const& notifier) { warning("Ignoring Notifier. Use AdaptiveGrid wrapper."); } /// Destructor, detaches from the notifier virtual ~Observer() { if (notifier_) notifier_->detach(this); } /// Copy constructor. Attaches this to the copied notifier Observer(Observer const& other) : notifier_(other.notifier_) { notifier_->attach(this); } /// Copy-assignment operator, copies the notifier and attaches this. Observer& operator=(Observer const& other) { if (&other != this) { notifier_->detach(this); notifier_ = other.notifier_; notifier_->attach(this); } return *this; } /// Set the Notifier* to nullptr. Used by the Notifier to avoid segfaults when destruction occurs /// out of order. void unset() final { notifier_ = nullptr; } /// Implementation of the interface method \ref ObserverInterface::update. /// Redirects to the \ref updateImpl method with additional \ref Tags parameters void update(Event e) final { updateImpl(e, Tags{}...); } protected: /// \brief Implementation of the update method in derived class // NOTE: The additional `Tags...` arguments can be used to distinguish // between multiple observers of the same event. virtual void updateImpl(Event e, Tags...) = 0; private: Notifier* notifier_ = nullptr; }; namespace Impl { template class ObserverSequenceImpl; /// Combination of multiple observers of the same event but with different tags template class ObserverSequenceImpl> : private Observer>... { public: template ObserverSequenceImpl(Notifiers&&... notifiers) : Observer>(FWD(notifiers))... {} }; } // end namespace Impl template using ObserverSequence = Impl::ObserverSequenceImpl>; } // end namespace AMDiS