diff --git a/src/amdis/DOFVector.hpp b/src/amdis/DOFVector.hpp index 1dc708b96a219bd2a4ee92fd9e48083a444cd531..dda9e23788fe7a4e6b4964e47013cbfe38520d58 100644 --- a/src/amdis/DOFVector.hpp +++ b/src/amdis/DOFVector.hpp @@ -66,7 +66,7 @@ namespace AMDiS DataTransferOperation op = DataTransferOperation::INTERPOLATE) : Coefficients(*basis) , Observer<event::preAdapt>(basis->gridView().grid()) - , Observer<event::adapt>(basis) + , Observer<event::adapt>(*basis) , Observer<event::postAdapt>(basis->gridView().grid()) , dataTransfer_(op, basis) , basis_(basis) @@ -129,12 +129,12 @@ namespace AMDiS } - Coefficients const& coefficients() const + Coefficients const& coefficients() const { return static_cast<Coefficients const&>(*this); } - Coefficients& coefficients() + Coefficients& coefficients() { return static_cast<Coefficients&>(*this); } diff --git a/src/amdis/Observer.hpp b/src/amdis/Observer.hpp index 865e5936e41f6793f26b51f0e3e4f2c2b91b3c9b..b0cdd1a1ba33ad37b403b812429606d08c269d06 100644 --- a/src/amdis/Observer.hpp +++ b/src/amdis/Observer.hpp @@ -1,33 +1,29 @@ #pragma once -#include <algorithm> -#include <memory> #include <set> -#include <type_traits> - -#include <dune/common/shared_ptr.hh> -#include <dune/common/typeutilities.hh> +#include <utility> #include <amdis/common/ConceptsBase.hpp> #include <amdis/common/Index.hpp> +#include <amdis/common/TypeTraits.hpp> namespace AMDiS { namespace event { - /** - * An event that is signaled before the actual adaption happens. Example: grid.preAdapt(). + /** + * 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 {}; @@ -40,6 +36,7 @@ namespace AMDiS public: virtual ~ObserverInterface() = default; virtual void update(Event e) = 0; + virtual void unset() = 0; }; @@ -66,6 +63,13 @@ namespace AMDiS class Notifier<Event> { public: + virtual ~Notifier() + { + // Remove remaining pointers to this to avoid segfaults + for (ObserverInterface<Event>* o : observers_) + o->unset(); + } + /// Call the \ref update method on all attached observers. void notify(Event const& e) { @@ -90,25 +94,19 @@ namespace AMDiS }; - /// Implementation of the \ref ObserverInterface + /// Implementation of the \ref ObserverInterface template <class Event, class... Tags> class Observer : public ObserverInterface<Event> { public: template <class Notifier> - Observer(std::shared_ptr<Notifier> notifier) - : notifier_(std::const_pointer_cast<std::remove_const_t<Notifier>>(std::move(notifier))) + Observer(Notifier const& notifier) + : notifier_(const_cast<Notifier*>(¬ifier)) { notifier_->attach(this); } - template <class Notifier, - class = void_t<decltype(std::declval<std::remove_const_t<Notifier>>().notify(std::declval<Event>()))> > - Observer(Notifier& notifier) - : Observer(Dune::stackobject_to_shared_ptr(notifier)) - {} - /// Destructor, detaches from the notifier virtual ~Observer() { @@ -134,6 +132,13 @@ namespace AMDiS return *this; } + /// Set the Notifier* to nullptr. Used by the Notifer 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 @@ -148,11 +153,11 @@ namespace AMDiS virtual void updateImpl(Event e, Tags...) = 0; private: - std::shared_ptr<Notifier<Event>> notifier_ = nullptr; + Notifier<Event>* notifier_ = nullptr; }; - namespace Impl + namespace Impl { template <class Event, class Tags> class ObserverSequenceImpl; diff --git a/src/amdis/linearalgebra/SparsityPattern.hpp b/src/amdis/linearalgebra/SparsityPattern.hpp index 93efde6052d7e1ea9188090115c917b13a9f211e..29cc43bb664ea5790245abb476f5c84aec38e9b5 100644 --- a/src/amdis/linearalgebra/SparsityPattern.hpp +++ b/src/amdis/linearalgebra/SparsityPattern.hpp @@ -9,7 +9,7 @@ namespace AMDiS { - /// \brief A general sparsity pattern implementation using the full pattern of the + /// \brief A general sparsity pattern implementation using the full pattern of the /// basis by adding all local indices template <class RowBasis, class ColBasis> class SparsityPattern @@ -88,8 +88,8 @@ namespace AMDiS void updateImpl3() { - rowBasis_ == colBasis_ - ? updateSameBasis() + rowBasis_ == colBasis_ + ? updateSameBasis() : updateDifferentBasis(); } diff --git a/src/amdis/linearalgebra/mtl/SlotSize.hpp b/src/amdis/linearalgebra/mtl/SlotSize.hpp index c04d8c3167457b9f05202f91e8871484c4f91ff0..ae093cebd49ea883ac55f36ce7ea2ca719f9ff08 100644 --- a/src/amdis/linearalgebra/mtl/SlotSize.hpp +++ b/src/amdis/linearalgebra/mtl/SlotSize.hpp @@ -63,16 +63,16 @@ namespace AMDiS protected: // update of the row basis, just update the dimension - void updateImpl(event::adapt e, index_t<0> i) final - { - rows_ = rowBasis_.dimension(); + void updateImpl(event::adapt e, index_t<0> i) final + { + rows_ = rowBasis_.dimension(); } - // estimate the number of columns by multiplying the maximal node size with the - // number of element surrounding a vertex. This number is approximated by the + // estimate the number of columns by multiplying the maximal node size with the + // number of element surrounding a vertex. This number is approximated by the // number of simplices surrounding a vertex in a kuhn tringulation - void updateImpl(event::adapt e, index_t<1> i) final - { + void updateImpl(event::adapt e, index_t<1> i) final + { cols_ = colBasis_.dimension(); estRowSize_ = std::min(cols_, colBasis_.maxSize() * surrounding_); } diff --git a/test/ObserverTest.cpp b/test/ObserverTest.cpp index 93e66256f259177886b824397da9f30a3ba47b57..c7482fd7cf014bd60a7cd42584bc174ed5f920c0 100644 --- a/test/ObserverTest.cpp +++ b/test/ObserverTest.cpp @@ -62,7 +62,7 @@ class B , public Notifier<event::bazEvent> { public: - B(std::shared_ptr<A> a) + B(A const& a) : Observer<event::fooEvent>(a) , Observer<event::bazEvent>(a) , a_(a) @@ -81,9 +81,9 @@ public: this->notify(e); } - std::shared_ptr<A> const& a() const { return a_; } - - std::shared_ptr<A> a_; + // Provide access to a_. This allows a class with access to B to observe A. + A const& a() const { return a_; } + A const& a_; }; class C @@ -91,8 +91,8 @@ class C , private Observer<event::bazEvent> { public: - C(std::shared_ptr<B> b) - : Observer<event::barEvent>(b->a()) + C(B const& b) + : Observer<event::barEvent>(b.a()) , Observer<event::bazEvent>(b) {} @@ -127,7 +127,7 @@ class E , public Observer<event::barEvent> { public: - E(std::shared_ptr<A> a, std::shared_ptr<D> d) + E(A const& a, D const& d) : Observer<event::fooEvent>(a) , Observer<event::barEvent>(d) {} @@ -145,51 +145,98 @@ public: } }; -/// Observing a class that inherits from Observer<T, Events...> more than once is not supported -// class F -// : public Observer<E, event::fooEvent> -// { -// public: -// F(std::shared_ptr<E> e) -// : Observer<E, event::fooEvent>(e) // Error -// {} -// }; +class F + : public ObserverSequence<event::barEvent, 2> +{ +public: + F(D const& d1, D const& d2) + : ObserverSequence<event::barEvent, 2>(d1, d2) + {} + + void updateImpl(event::barEvent, index_t<0> i) override + { + std::cout << "F::update(bar, 0)\n"; + testValue += "F.bar0 "; + } + + void updateImpl(event::barEvent, index_t<1> i) override + { + std::cout << "F::update(bar, 1)\n"; + testValue += "F.bar1 "; + } +}; /// Test the observer hierarchy. /** - * There are 4 hierarchies tested. + * There are 5 hierarchies tested. * A::foo(): A -> B, E (multiple observers [B,E] of one event) - * A::bar(): A -> (B) -> C (indirect observer [C of A]) - * A::baz(): A -> B -> C (simultanious observer and signaller of an event [B]) + * A::bar(): A -> (B) -> C (indirect observer [C of A] via access function) + * A::baz(): A -> B -> C (simultanious observer and notifier of an event [B]) * D::bar(): D -> E (observer of multiple classes [E]) + * F::bar(): D1/D2 -> F (observer of multiple classes with the same event [F]) */ 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); - auto d = std::make_shared<D>(); - auto e = std::make_shared<E>(a, d); + A a; std::string ref, refOpt; + { + B b(a); + C c(b); + D d; + E e(a, d); + D d1; + D d2; + D d3; + F f1(d1, d2); + F f2(d3, d3); + + a.foo(); + ref = "A.foo B.foo E.foo "; + refOpt = "A.foo E.foo B.foo "; // Order of b.foo() and e.foo() unspecified + AMDIS_TEST(checkAndResetTestValue("Test: both b and e are notified", ref, refOpt)); + + a.bar(); + ref = "A.bar C.bar "; + AMDIS_TEST(checkAndResetTestValue("Test: c is notified", ref)); + + a.baz(); + ref = "A.baz B.baz C.baz "; + AMDIS_TEST(checkAndResetTestValue("Test: b and c are notified in order", ref)); + + d.bar(); + ref = "D.bar E.bar "; + AMDIS_TEST(checkAndResetTestValue("Test: e is notified and the correct overload is chosen", ref)); + + d1.bar(); + ref = "D.bar F.bar0 "; + AMDIS_TEST(checkAndResetTestValue("Test: both d1 and d2 notify f1: d1", ref)); + d2.bar(); + ref = "D.bar F.bar1 "; + AMDIS_TEST(checkAndResetTestValue("Test: both d1 and d2 notify f1: d2", ref)); + + d3.bar(); + ref = "D.bar F.bar0 F.bar1 "; + refOpt = "D.bar F.bar1 F.bar0 "; + AMDIS_TEST(checkAndResetTestValue("Test: d3 notifies f2 twice", ref, refOpt)); + } - a->foo(); - ref = "A.foo B.foo E.foo "; - refOpt = "A.foo E.foo B.foo "; // Order of b->foo() and e->foo() unspecified - AMDIS_TEST(checkAndResetTestValue("A:foo()", ref, refOpt)); - - a->bar(); - ref = "A.bar C.bar "; - AMDIS_TEST(checkAndResetTestValue("A:bar()", ref)); - - a->baz(); - ref = "A.baz B.baz C.baz "; - AMDIS_TEST(checkAndResetTestValue("A:baz()", ref)); - - d->bar(); - ref = "D.bar E.bar "; - AMDIS_TEST(checkAndResetTestValue("D:bar()", ref)); + // Observers went out of scope + try { + a.foo(); + ref = "A.foo "; + AMDIS_TEST(checkAndResetTestValue("Test: observers detach properly: foo", ref)); + a.bar(); + ref = "A.bar "; + AMDIS_TEST(checkAndResetTestValue("Test: observers detach properly: bar", ref)); + a.baz(); + ref = "A.baz "; + AMDIS_TEST(checkAndResetTestValue("Test: observers detach properly: baz", ref)); + } catch(...) { + std::cout << "Observer did not detach properly and threw an exception\n"; + Impl::num_errors()++; + } return report_errors(); }