Skip to content
Snippets Groups Projects
Commit dd8d79c9 authored by Praetorius, Simon's avatar Praetorius, Simon
Browse files

Simplify observer

parent bbc64e6b
No related branches found
No related tags found
No related merge requests found
......@@ -27,27 +27,6 @@
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
template <class HostGrid>
class AdaptiveGridFamily;
......@@ -65,7 +44,7 @@ namespace AMDiS
class AdaptiveGrid
: public Dune::GridDefaultImplementation<
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>;
......
......@@ -28,12 +28,6 @@ namespace AMDiS
template <class PB>
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
/**
......@@ -43,11 +37,12 @@ namespace AMDiS
template <class GB, class T = double>
class DOFVector
: 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 Super = VectorBase<GB, VectorBackend<BackendTraits<GB,T>>>;
using Obs = Observer<GB, event::preAdapt, event::basisUpdate<GB>, event::postAdapt>;
public:
using Backend = VectorBackend<BackendTraits<GB,T>>;
......@@ -68,7 +63,9 @@ namespace AMDiS
DOFVector(std::shared_ptr<GB> const& basis,
DataTransferOperation op = DataTransferOperation::INTERPOLATE)
: Super(basis)
, Obs(basis)
, Observer<event::preAdapt>(basis->gridView().grid())
, Observer<event::adapt>(*basis)
, Observer<event::postAdapt>(basis->gridView().grid())
, dataTransfer_(op, basis)
, basis_(basis)
{}
......@@ -165,24 +162,27 @@ namespace AMDiS
setDataTransfer(newDataTransfer(op, basis_));
}
protected:
/// Override of Observer update(event::preAdapt) method. Redirects to preAdapt method of the
/// \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
/// \ref DataTransfer object.
void update(event::basisUpdate<GB> const&) override
void updateImpl(event::adapt e) override
{
assert(e.value);
this->resize();
dataTransfer_.adapt(*this);
}
/// Override of Observer update(event::postAdapt) method. Redirects to postAdapt method of the
/// \ref DataTransfer object.
void update(event::postAdapt const&) override
void updateImpl(event::postAdapt) override
{
dataTransfer_.postAdapt(*this);
}
......
......@@ -12,158 +12,34 @@
namespace AMDiS
{
namespace Impl
namespace event
{
// forward declaration
template <class Event>
class ObserverInterface;
/**
* 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; };
template <class Event>
class SignalBase
{
public:
/// Attaches an observer to this class. This method will be called by all observers with
/// with themselves as argument.
void attachObserver(ObserverInterface<Event>* o) const
{
observers_.push_back(o);
}
/// 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
{
auto it = std::find(observers_.begin(), observers_.end(), o);
if (it != observers_.end())
observers_.erase(it);
}
/// 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
/**
* 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 Event>
class ObserverInterface
{
public:
virtual ~ObserverInterface() = default;
virtual void update(Event e) = 0;
};
/// \brief Mixin for signaling of certain events.
......@@ -176,57 +52,110 @@ namespace AMDiS
* to the instance of T (see Observer)
*/
template <class Event, class... Events>
class Signals
: public Impl::SignalBase<Event>
, public Signals<Events...>
class Notifier
: public Notifier<Event>
, public Notifier<Events...>
{
public:
using Impl::SignalBase<Event>::notify;
using Signals<Events...>::notify;
using Notifier<Event>::notify;
using Notifier<Events...>::notify;
};
template <class Event>
class Signals<Event>
: public Impl::SignalBase<Event>
class Notifier<Event>
{
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.
/**
* 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>
/// Implementation of the \ref ObserverInterface
template <class Event, class... Tags>
class Observer
: public Impl::ObserverBase<Events>...
: public ObserverInterface<Event>
{
template <class E>
friend class Impl::ObserverBase;
public:
Observer(std::shared_ptr<Subject const> const& s)
: Impl::ObserverBase<Events>(s)...
, subject_(std::move(s))
{}
template <class Notifier>
Observer(Notifier const& notifier)
: 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)
: Observer(std::const_pointer_cast<Subject const>(s))
{}
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:
std::shared_ptr<Subject const> subject_ = nullptr;
Notifier<Event>* notifier_ = nullptr;
};
} // end namespace AMDiS
......@@ -45,16 +45,6 @@ namespace Dune
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
*
......@@ -65,15 +55,11 @@ namespace AMDiS
*/
template <class PB>
class ParallelGlobalBasis
: public Observer<typename PB::GridView::Grid, event::adapt>
, public Signals<event::basisUpdate<ParallelGlobalBasis<PB>>>
: public Notifier<event::adapt>
, private Observer<event::adapt>
{
using Subject = typename PB::GridView::Grid;
using Super = Observer<Subject, event::adapt>;
using Self = ParallelGlobalBasis<PB>;
struct DummyImpl {};
public:
/// Pre-basis providing the implementation details
......@@ -109,6 +95,7 @@ namespace AMDiS
/// Type of the communicator
using Comm = typename BackendTraits<Self>::Comm;
struct DummyImpl {};
using ADH = Dune::AdaptDataHandle<Grid, DummyImpl>;
......@@ -124,7 +111,7 @@ namespace AMDiS
template <class... Args,
Dune::Functions::enableIfConstructible<PreBasis, Args...> = 0>
ParallelGlobalBasis(std::string const& name, Grid const& grid, Args&&... args)
: Super(Dune::stackobject_to_shared_ptr(grid))
: Observer<event::adapt>(grid)
, preBasis_(FWD(args)...)
{
static_assert(Dune::models<Dune::Functions::Concept::PreBasis<GridView>, PreBasis>(),
......@@ -172,16 +159,6 @@ namespace AMDiS
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
/**
* This will update the indexing information of the global basis as well as the communicator.
......@@ -245,6 +222,17 @@ namespace AMDiS
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:
PreBasis preBasis_;
PrefixPath prefixPath_ = {};
......
......@@ -6,7 +6,7 @@
#include "Tests.hpp"
using namespace AMDiS;
namespace AMDiS {
namespace event {
struct fooEvent {};
......@@ -14,6 +14,8 @@ namespace event {
struct bazEvent {};
}
} // end namespace AMDiS
std::string testValue = "";
bool checkAndResetTestValue(std::string testName, std::string ref, std::string refOpt = "X")
......@@ -23,8 +25,10 @@ bool checkAndResetTestValue(std::string testName, std::string ref, std::string r
return result;
}
using namespace AMDiS;
class A
: public Signals<event::fooEvent, event::barEvent, event::bazEvent>
: public Notifier<event::fooEvent, event::barEvent, event::bazEvent>
{
public:
void foo()
......@@ -53,43 +57,52 @@ public:
};
class B
: public Observer<A, event::fooEvent, event::bazEvent>
, public Signals<event::bazEvent>
: private Observer<event::fooEvent>
, private Observer<event::bazEvent>
, public Notifier<event::bazEvent>
{
public:
B(std::shared_ptr<A> a)
: Observer<A, event::fooEvent, event::bazEvent>(a)
: Observer<event::fooEvent>(*a)
, Observer<event::bazEvent>(*a)
, a_(a)
{}
void update(event::fooEvent const& e) override
void updateImpl(event::fooEvent) override
{
std::cout << "B::update(foo)\n";
testValue += "B.foo ";
}
void update(event::bazEvent const& e) override
void updateImpl(event::bazEvent e) override
{
std::cout << "B::update(baz)\n";
testValue += "B.baz ";
this->notify(e);
}
std::shared_ptr<A> const& a() const { return a_; }
std::shared_ptr<A> a_;
};
class C
: public Observer<B, event::barEvent, event::bazEvent>
: private Observer<event::barEvent>
, private Observer<event::bazEvent>
{
public:
C(std::shared_ptr<B> b)
: Observer<B, event::barEvent, event::bazEvent>(b)
: Observer<event::barEvent>(*b->a())
, Observer<event::bazEvent>(*b)
{}
void update(event::barEvent const& e) override
void updateImpl(event::barEvent) override
{
std::cout << "C::update(bar)\n";
testValue += "C.bar ";
}
void update(event::bazEvent const& e) override
void updateImpl(event::bazEvent) override
{
std::cout << "C::update(baz)\n";
testValue += "C.baz ";
......@@ -97,7 +110,7 @@ public:
};
class D
: public Signals<event::barEvent>
: public Notifier<event::barEvent>
{
public:
void bar()
......@@ -110,22 +123,22 @@ public:
};
class E
: public Observer<A, event::fooEvent>
, public Observer<D, event::barEvent>
: public Observer<event::fooEvent>
, public Observer<event::barEvent>
{
public:
E(std::shared_ptr<A> a, std::shared_ptr<D> d)
: Observer<A, event::fooEvent>(a)
, Observer<D, event::barEvent>(d)
: Observer<event::fooEvent>(*a)
, Observer<event::barEvent>(*d)
{}
void update(event::fooEvent const& e) override
void updateImpl(event::fooEvent) override
{
std::cout << "E::update(foo)\n";
testValue += "E.foo ";
}
void update(event::barEvent const& e) override
void updateImpl(event::barEvent) override
{
std::cout << "E::update(bar)\n";
testValue += "E.bar ";
......@@ -152,6 +165,8 @@ public:
*/
int main()
{
using namespace AMDiS;
auto a = std::make_shared<A>();
auto b = std::make_shared<B>(a);
auto c = std::make_shared<C>(b);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment