Commit dd8d79c9 authored by Praetorius, Simon's avatar Praetorius, Simon
Browse files

Simplify observer

parent bbc64e6b
...@@ -27,27 +27,6 @@ ...@@ -27,27 +27,6 @@
namespace AMDiS namespace AMDiS
{ {
namespace event
{
/** Event generated from an AdaptiveGrid when calling preAdapt(). It contains the return value
* of preAdapt() as its 'mightCoarsen' member and is passed to registered observers after
* calling preAdapt on the underlying grid.
**/
struct preAdapt { bool mightCoarsen; };
/** Event generated from an AdaptiveGrid when calling adapt(). Its 'adapted' member contains the
* value true if either preAdapt() or adapt() returned true. This event is passed to registered
* observers after calling adapt on the underlying grid.
**/
struct adapt { bool adapted; };
/** Event generated from an AdaptiveGrid when calling postAdapt().This event is passed to
* registered observers after calling postAdapt on the underlying grid.
**/
struct postAdapt {};
}
// forward declaration // forward declaration
template <class HostGrid> template <class HostGrid>
class AdaptiveGridFamily; class AdaptiveGridFamily;
...@@ -65,7 +44,7 @@ namespace AMDiS ...@@ -65,7 +44,7 @@ namespace AMDiS
class AdaptiveGrid class AdaptiveGrid
: public Dune::GridDefaultImplementation< : public Dune::GridDefaultImplementation<
HG::dimension, HG::dimensionworld, typename HG::ctype, AdaptiveGridFamily<HG> > HG::dimension, HG::dimensionworld, typename HG::ctype, AdaptiveGridFamily<HG> >
, public Signals<event::preAdapt, event::adapt, event::postAdapt> , public Notifier<event::preAdapt, event::adapt, event::postAdapt>
{ {
using Self = AdaptiveGrid<HG>; using Self = AdaptiveGrid<HG>;
......
...@@ -28,12 +28,6 @@ namespace AMDiS ...@@ -28,12 +28,6 @@ namespace AMDiS
template <class PB> template <class PB>
class ParallelGlobalBasis; class ParallelGlobalBasis;
namespace event
{
struct preAdapt;
struct postAdapt;
template <class B> struct basisUpdate;
}
/// \brief The basic container that stores a base vector and a corresponding basis /// \brief The basic container that stores a base vector and a corresponding basis
/** /**
...@@ -43,11 +37,12 @@ namespace AMDiS ...@@ -43,11 +37,12 @@ namespace AMDiS
template <class GB, class T = double> template <class GB, class T = double>
class DOFVector class DOFVector
: public VectorBase<GB, VectorBackend<BackendTraits<GB,T>>> : public VectorBase<GB, VectorBackend<BackendTraits<GB,T>>>
, public Observer<GB, event::preAdapt, event::basisUpdate<GB>, event::postAdapt> , private Observer<event::preAdapt>
, private Observer<event::adapt>
, private Observer<event::postAdapt>
{ {
using Self = DOFVector; using Self = DOFVector;
using Super = VectorBase<GB, VectorBackend<BackendTraits<GB,T>>>; using Super = VectorBase<GB, VectorBackend<BackendTraits<GB,T>>>;
using Obs = Observer<GB, event::preAdapt, event::basisUpdate<GB>, event::postAdapt>;
public: public:
using Backend = VectorBackend<BackendTraits<GB,T>>; using Backend = VectorBackend<BackendTraits<GB,T>>;
...@@ -68,7 +63,9 @@ namespace AMDiS ...@@ -68,7 +63,9 @@ namespace AMDiS
DOFVector(std::shared_ptr<GB> const& basis, DOFVector(std::shared_ptr<GB> const& basis,
DataTransferOperation op = DataTransferOperation::INTERPOLATE) DataTransferOperation op = DataTransferOperation::INTERPOLATE)
: Super(basis) : Super(basis)
, Obs(basis) , Observer<event::preAdapt>(basis->gridView().grid())
, Observer<event::adapt>(*basis)
, Observer<event::postAdapt>(basis->gridView().grid())
, dataTransfer_(op, basis) , dataTransfer_(op, basis)
, basis_(basis) , basis_(basis)
{} {}
...@@ -165,24 +162,27 @@ namespace AMDiS ...@@ -165,24 +162,27 @@ namespace AMDiS
setDataTransfer(newDataTransfer(op, basis_)); setDataTransfer(newDataTransfer(op, basis_));
} }
protected:
/// Override of Observer update(event::preAdapt) method. Redirects to preAdapt method of the /// Override of Observer update(event::preAdapt) method. Redirects to preAdapt method of the
/// \ref DataTransfer object. /// \ref DataTransfer object.
void update(event::preAdapt const& e) override void updateImpl(event::preAdapt e) override
{ {
dataTransfer_.preAdapt(*this, e.mightCoarsen); dataTransfer_.preAdapt(*this, e.value);
} }
/// Override of Observer update(event::adapt) method. Redirects to adapt method of the /// Override of Observer update(event::adapt) method. Redirects to adapt method of the
/// \ref DataTransfer object. /// \ref DataTransfer object.
void update(event::basisUpdate<GB> const&) override void updateImpl(event::adapt e) override
{ {
assert(e.value);
this->resize(); this->resize();
dataTransfer_.adapt(*this); dataTransfer_.adapt(*this);
} }
/// Override of Observer update(event::postAdapt) method. Redirects to postAdapt method of the /// Override of Observer update(event::postAdapt) method. Redirects to postAdapt method of the
/// \ref DataTransfer object. /// \ref DataTransfer object.
void update(event::postAdapt const&) override void updateImpl(event::postAdapt) override
{ {
dataTransfer_.postAdapt(*this); dataTransfer_.postAdapt(*this);
} }
......
...@@ -12,158 +12,34 @@ ...@@ -12,158 +12,34 @@
namespace AMDiS namespace AMDiS
{ {
namespace Impl namespace event
{ {
// forward declaration /**
template <class Event> * An event that is signaled before the actual adaption happens. Example: grid.preAdapt().
class ObserverInterface; * The \ref value might indicate whether any pre-processing is necessary.
**/
struct preAdapt { bool value = true; };
template <class Event> /**
class SignalBase * An event that is called directly of the adaption. Example: grid.adapt().
{ * The \ref value indicates whether something is changed during adaption.
public: **/
/// Attaches an observer to this class. This method will be called by all observers with struct adapt { bool value = true; };
/// with themselves as argument.
void attachObserver(ObserverInterface<Event>* o) const /**
{ * An event that is called after adaption to indicate the start of a clean-up phase.
observers_.push_back(o); **/
} struct postAdapt {};
}
/// Detaches an observer to this class. This method will be called by all observers with
/// with themselves as argument.
void detachObserver(ObserverInterface<Event>* o) const template <class Event>
{ class ObserverInterface
auto it = std::find(observers_.begin(), observers_.end(), o); {
if (it != observers_.end()) public:
observers_.erase(it); virtual ~ObserverInterface() = default;
} virtual void update(Event e) = 0;
};
/// Notify all observers that have called attachObserver but have not called detachObserver
void notify(Event const& e) const
{
for (ObserverInterface<Event>* o : observers_)
o->update(e);
}
private:
/// List of observers that need to be notified in case of an event
// NOTE: this list is mutable, since the notification list itself is not part
// of the internal state of the object signaling the event.
mutable std::list<ObserverInterface<Event>*> observers_;
};
template <class Event>
class ObserverInterface
{
public:
virtual ~ObserverInterface() = default;
/// Attach the observer to a subject. It will then receive notifications from the subject when
/// an event of type Event it triggered.
void attach(std::shared_ptr<SignalBase<Event> const> const& subject)
{
if (bool(subject))
subject->attachObserver(this);
}
/// Detach the observer from the subject. It will then no longer receive notifications. This
/// must be called before the observer is deleted.
void detach(std::shared_ptr<SignalBase<Event> const> const& subject)
{
if (bool(subject))
subject->detachObserver(this);
}
/// This method will be called by the subject when triggering the event.
virtual void update(Event const&)
{
error_exit("Method must be overridden by derived class");
}
};
template <class Event>
class ObserverBase
: virtual public ObserverInterface<Event>
{
using Self = ObserverBase;
private:
ObserverBase() = default;
// use subject if Event is handled directly
template <class S,
REQUIRES(std::is_base_of<SignalBase<Event>, S>::value)>
ObserverBase(std::shared_ptr<S const> subject, Dune::PriorityTag<2>)
: subject_(std::move(subject))
{
this->attach(subject_);
}
// get next subject in hierarchy if Event is not handled
template <class Observer,
class = void_t<decltype(std::declval<Observer>().subject_)> >
ObserverBase(std::shared_ptr<Observer const> const& o, Dune::PriorityTag<1>)
: ObserverBase(o->subject_)
{}
// non-observable type
template <class T>
ObserverBase(std::shared_ptr<T const> const& other, Dune::PriorityTag<0>)
{
/* fallback implementation */
}
public:
template <class T>
ObserverBase(std::shared_ptr<T const> const& arg)
: ObserverBase(arg, Dune::PriorityTag<42>{})
{}
/// Copy constructor
ObserverBase(Self const& that)
: subject_(that.subject_)
{
this->attach(subject_);
}
/// Move constructor
ObserverBase(Self&& that)
{
swap(*this, that);
}
/// Destructor. Detaches observer from the subject
~ObserverBase() override
{
this->detach(subject_);
}
/// Copy/Move assignment
Self& operator=(Self that)
{
swap(*this, that);
return *this;
}
/// Swaps the contents of lhs and rhs
friend void swap(Self& lhs, Self& rhs)
{
using std::swap;
lhs.detach(lhs.subject_);
rhs.detach(rhs.subject_);
swap(lhs.subject_, rhs.subject_);
lhs.attach(lhs.subject_);
rhs.attach(rhs.subject_);
}
private:
/// The observed subject
std::shared_ptr<SignalBase<Event> const> subject_ = nullptr;
};
} // end namespace Impl
/// \brief Mixin for signaling of certain events. /// \brief Mixin for signaling of certain events.
...@@ -176,57 +52,110 @@ namespace AMDiS ...@@ -176,57 +52,110 @@ namespace AMDiS
* to the instance of T (see Observer) * to the instance of T (see Observer)
*/ */
template <class Event, class... Events> template <class Event, class... Events>
class Signals class Notifier
: public Impl::SignalBase<Event> : public Notifier<Event>
, public Signals<Events...> , public Notifier<Events...>
{ {
public: public:
using Impl::SignalBase<Event>::notify; using Notifier<Event>::notify;
using Signals<Events...>::notify; using Notifier<Events...>::notify;
}; };
template <class Event> template <class Event>
class Signals<Event> class Notifier<Event>
: public Impl::SignalBase<Event>
{ {
public: public:
using Impl::SignalBase<Event>::notify; /// Call the \ref update method on all attached observers.
void notify(Event const& e)
{
for (ObserverInterface<Event>* o : observers_)
o->update(e);
}
/// Attach a new observer that gets called on \ref notify
void attach(ObserverInterface<Event>* o)
{
observers_.push_back(o);
}
/// Detaches the passed observer from the list, if stored.
void detach(ObserverInterface<Event>* o)
{
auto it = std::find(observers_.begin(), observers_.end(), o);
if (it != observers_.end())
observers_.erase(it);
}
private:
std::list<ObserverInterface<Event>*> observers_;
}; };
/// \brief Mixin for reacting to certain events. /// Implementation of the \ref ObserverInterface
/** template <class Event, class... Tags>
* Derived classes can react to events by implementing the functions `update(Event)` for
* all Events specified in the template parameter list, whose origin is an instance of class
* `Subject` specified in the constructor to Observer and any indirectly observed class instances.
* Observed instances may include:
* - the instance of class `Subject` passed to the constructor
* - the instance passed to the constructor of `Subject::Observer<S>` if Subject inherits from
* \ref Observer<S>
* - all other instances indirectly accessible in the above way
*
* For each event E the first object in the above hierarchy that implements \ref Signals<Es...> with E
* included in Es... will be observed by this class.
*/
template <class Subject, class... Events>
class Observer class Observer
: public Impl::ObserverBase<Events>... : public ObserverInterface<Event>
{ {
template <class E>
friend class Impl::ObserverBase;
public: public:
Observer(std::shared_ptr<Subject const> const& s) template <class Notifier>
: Impl::ObserverBase<Events>(s)... Observer(Notifier const& notifier)
, subject_(std::move(s)) : notifier_(const_cast<Notifier*>(&notifier))
{} {
notifier_->attach(this);
}
/// Destructor, detaches from the notifier
virtual ~Observer()
{
assert(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)
{
notifier_ = other.notifier_;
notifier_->attach(this);
return *this;
}
/// Move assignment operator, implemented in terms of the move
/// assignment operator
Observer(Observer&& other)
{
*this = std::move(other);
}
/// Move-assignment operator, copies the notifier and attaches this.
Observer& operator=(Observer&& other)
{
notifier_ = other.notifier_;
notifier_->attach(this);
return *this;
}
/// 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{}...);
}
Observer(std::shared_ptr<Subject> const& s) protected:
: Observer(std::const_pointer_cast<Subject const>(s)) /// \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: private:
std::shared_ptr<Subject const> subject_ = nullptr; Notifier<Event>* notifier_ = nullptr;
}; };
} // end namespace AMDiS } // end namespace AMDiS
...@@ -45,16 +45,6 @@ namespace Dune ...@@ -45,16 +45,6 @@ namespace Dune
namespace AMDiS namespace AMDiS
{ {
namespace event
{
template <class B>
struct basisUpdate
{
using Basis = B;
Basis const& basis;
};
}
/** /**
* \brief Parallel global basis defined on a (sequential) pre-basis * \brief Parallel global basis defined on a (sequential) pre-basis
* *
...@@ -65,15 +55,11 @@ namespace AMDiS ...@@ -65,15 +55,11 @@ namespace AMDiS
*/ */
template <class PB> template <class PB>
class ParallelGlobalBasis class ParallelGlobalBasis
: public Observer<typename PB::GridView::Grid, event::adapt> : public Notifier<event::adapt>
, public Signals<event::basisUpdate<ParallelGlobalBasis<PB>>> , private Observer<event::adapt>
{ {
using Subject = typename PB::GridView::Grid;
using Super = Observer<Subject, event::adapt>;
using Self = ParallelGlobalBasis<PB>; using Self = ParallelGlobalBasis<PB>;
struct DummyImpl {};
public: public:
/// Pre-basis providing the implementation details /// Pre-basis providing the implementation details
...@@ -109,6 +95,7 @@ namespace AMDiS ...@@ -109,6 +95,7 @@ namespace AMDiS
/// Type of the communicator /// Type of the communicator
using Comm = typename BackendTraits<Self>::Comm; using Comm = typename BackendTraits<Self>::Comm;
struct DummyImpl {};
using ADH = Dune::AdaptDataHandle<Grid, DummyImpl>; using ADH = Dune::AdaptDataHandle<Grid, DummyImpl>;
...@@ -124,7 +111,7 @@ namespace AMDiS ...@@ -124,7 +111,7 @@ namespace AMDiS
template <class... Args, template <class... Args,
Dune::Functions::enableIfConstructible<PreBasis, Args...> = 0> Dune::Functions::enableIfConstructible<PreBasis, Args...> = 0>
ParallelGlobalBasis(std::string const& name, Grid const& grid, Args&&... args) ParallelGlobalBasis(std::string const& name, Grid const& grid, Args&&... args)
: Super(Dune::stackobject_to_shared_ptr(grid)) : Observer<event::adapt>(grid)
, preBasis_(FWD(args)...) , preBasis_(FWD(args)...)
{ {
static_assert(Dune::models<Dune::Functions::Concept::PreBasis<GridView>, PreBasis>(), static_assert(Dune::models<Dune::Functions::Concept::PreBasis<GridView>, PreBasis>(),
...@@ -172,16 +159,6 @@ namespace AMDiS ...@@ -172,16 +159,6 @@ namespace AMDiS
return preBasis_; return preBasis_;
} }
/// Updates the underlying basis when event::adapt is triggered by the observed grid
void update(event::adapt const& e) override
{
if (e.adapted) {
update(gridView());
event::basisUpdate<Self> eNew{*this};
this->notify(eNew);
}
}
/// \brief Update the stored grid view /// \brief Update the stored grid view
/** /**
* This will update the indexing information of the global basis as well as the communicator. * This will update the indexing information of the global basis as well as the communicator.
...@@ -245,6 +222,17 @@ namespace AMDiS ...@@ -245,6 +222,17 @@ namespace AMDiS
return ADH{}; return ADH{};
} }
protected:
/// Updates the underlying basis when event::adapt is triggered by the observed grid
void updateImpl(event::adapt e) override
{
if (e.value) {
update(gridView());
this->notify(e);
}
}
protected: protected:
PreBasis preBasis_; PreBasis preBasis_;
PrefixPath prefixPath_ = {}; PrefixPath prefixPath_ = {};
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#include "Tests.hpp" #include "Tests.hpp"
using namespace AMDiS; namespace AMDiS {
namespace event { namespace event {
struct fooEvent {}; struct fooEvent {};
...@@ -14,6 +14,8 @@ namespace event { ...@@ -14,6 +14,8 @@ namespace event {
struct bazEvent {}; struct bazEvent {};