diff --git a/doc/design/architecture/Subsystems.txt b/doc/design/architecture/Subsystems.txt index db69d937e..9c1cf1747 100644 --- a/doc/design/architecture/Subsystems.txt +++ b/doc/design/architecture/Subsystems.txt @@ -150,7 +150,7 @@ Lifecycle Events ~~~~~~~~~~~~~~~~ The Application as a whole conducts a well defined lifecycle; whenever transitioning to the next phase, a _Lifecycle Event_ is issued. Components may register a notification hook with the central _Lifecycle Manager_ -(see 'include/lifecycle.h) to be invoked whenever a specific event is emitted. The process of registration +(see 'include/lifecycle.h') to be invoked whenever a specific event is emitted. The process of registration can be simplified by planting a static variable of type `lumiera::LifecycleHook`. WARNING: A callback enrolled this way needs to be callable at the respective point in the lifecycle, diff --git a/src/lib/depend-inject.hpp b/src/lib/depend-inject.hpp index b7d90a424..67ae8874d 100644 --- a/src/lib/depend-inject.hpp +++ b/src/lib/depend-inject.hpp @@ -103,6 +103,7 @@ ** persistent history and UNDO. ** ** @see DependencyConfiguration_test + ** @see SingletonSubclass_test ** @see subsys.hpp */ @@ -117,6 +118,7 @@ #include "lib/error.hpp" #include "lib/nocopy.hpp" #include "lib/depend2.hpp" +#include "lib/meta/trait.hpp" #include "lib/meta/function.hpp" #include "lib/sync-classlock.hpp" @@ -179,14 +181,11 @@ namespace lib { static void useSingleton(FUN&& ctor) { + using Fun = typename SubclassFactory::Fun; using Sub = typename SubclassFactory::Sub; __assert_compatible(); - static InstanceHolder singleton; - installFactory ([ctor]() - { - return singleton.buildInstance (ctor); - }); + installFactory (buildCustomSingleton (forward (ctor))); } @@ -329,6 +328,7 @@ namespace lib { static_assert (meta::_Fun(), "Need a Lambda or Function object to create a heap allocated instance"); + using Fun = typename meta::_Fun::Functor; // suitable type to store for later invocation using Res = typename meta::_Fun::Ret; using Sub = typename meta::Strip::TypePlain; @@ -348,6 +348,22 @@ namespace lib { Depend::factory = move (otherFac); } + /** wrap custom factory function to plant a singleton instance + * @remark call through this intermediary function because we need to capture a _copy_ of the functor, + * to invoke it later, on-demand. Especially we need the ability to change the type of this functor, + * since sometimes the argument is passed as function reference, which can not be instantiated, + * but needs to be wrapped into a std::function. */ + template + static Factory + buildCustomSingleton (FUN&& ctor) + { + static InstanceHolder singleton; + return ([ctor]() // copy of ctor in the closure + { + return singleton.buildInstance (ctor); + }); + } + static void temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, Factory&& newFac) { diff --git a/src/lib/meta/function.hpp b/src/lib/meta/function.hpp index 9e6d9fe6f..1075c197c 100644 --- a/src/lib/meta/function.hpp +++ b/src/lib/meta/function.hpp @@ -67,12 +67,15 @@ namespace meta{ /** * Helper for uniform access to function signature types. * Extract the type information contained in a function or functor type, - * so it can be manipulated by metaprogramming. The embedded typedefs - * allow to pick up the return type, the sequence of argument types - * and the bare function signature type. This template works on + * so it can be manipulated by metaprogramming. This template works on * anything _function like_, irrespective if the parameter is given * as function reference, function pointer, member function pointer, - * functor object, `std::function` or lambda. + * functor object, `std::function` or lambda. The embedded typedefs + * allow to pick up + * - `Ret` : the return type + * - `Args`: the sequence of argument types as type sequence `Types` + * - `Sig` : the bare function signature type + * - `Functor` : corresponding Functor type which can be instantiated or copied. * * This template can also be used in metaprogramming with `enable_if` to enable * some definition or specialisation only if a function-like type was detected; thus diff --git a/tests/15library.tests b/tests/15library.tests index b00d48868..8f7e8777d 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -686,15 +686,15 @@ END TEST "SingletonTestMock_test" SingletonTestMock_test < @@ -73,12 +73,7 @@ namespace test{ struct SubSub : Sub - { - /** marker typedef for Depend4Test, - * allowing to pick the correct Depend - * to apply the instrumentation with the test mock. */ - typedef Sub ServiceInterface; - }; + { }; struct SubSubSub : SubSub @@ -111,7 +106,6 @@ namespace test{ verify_SubclassCreation(); verify_FactoryDefinition_is_sticky(); verify_customFactory(); - verify_temporaryReplacement(); verify_automaticReplacement(); } @@ -131,9 +125,12 @@ namespace test{ void verify_SubclassCreation() { - Depend specialAccessor(buildSingleton()); + Depend specialAccessor; Depend genericAccessor; + // configure singleton subclass (prior to first use) + DependInject::useSingleton(); + SubSub& oSub = specialAccessor(); Sub& o = genericAccessor(); @@ -155,13 +152,18 @@ namespace test{ SubSub& yetAnotherInstance = yetAnotherSpecialAccessor(); CHECK ( INSTANCEOF (SubSubSub, &yetAnotherInstance)); + + // both refer to the same configuration and thus access the singleton + CHECK (isSameObject (oSub, yetAnotherInstance)); } void verify_customFactory() { - Depend customisedAccessor(&customFactoryFunction); + DependInject::useSingleton (customFactoryFunction); + + Depend customisedAccessor; Depend otherSpecialAccessor; SubSub& oSub = otherSpecialAccessor(); @@ -175,47 +177,12 @@ namespace test{ CHECK (MAX_ID + 10 == oSubS.instanceID_); } - static void* + static SubSubSub* customFactoryFunction (void) { - static SubSubSub specialInstance; - // NOTE: the factory function is responsible - // for managing the instance's lifecycle - - specialInstance.instanceID_ = MAX_ID + 10; - return &specialInstance; - } - - - - void - verify_temporaryReplacement() - { - typedef Depend GenericAccessor; - - GenericAccessor genericAccessor; - Sub& original = genericAccessor(); - uint oID = original.instanceID_; - - SubSubSub mockObject; - Sub* shaddowedOriginal = GenericAccessor::injectReplacement (&mockObject); - - Sub& replacement = genericAccessor(); - CHECK ( isSameObject (replacement, mockObject)); - CHECK (!isSameObject (original, replacement)); - CHECK ( isSameObject (original, *shaddowedOriginal)); - - Depend special; - Depend custom; - - CHECK(!isSameObject (replacement, special() )); - CHECK(!isSameObject (replacement, custom() )); - - GenericAccessor::injectReplacement (shaddowedOriginal); - - Sub& nextFetch = genericAccessor(); - CHECK (isSameObject (original, nextFetch)); - CHECK (oID == nextFetch.instanceID_); + SubSubSub* specialInstance = new SubSubSub; + specialInstance->instanceID_ = MAX_ID + 10; + return specialInstance; } @@ -227,8 +194,26 @@ namespace test{ Sub& original = genericAccessor(); uint oID = original.instanceID_; - { - Depend4Test withinThisScope; + {////////////////////////////////////////////////////TEST-Scope + DependInject::Local mockObject; + + Sub& replacement = genericAccessor(); + CHECK ( isSameObject (replacement, *mockObject)); + CHECK (!isSameObject (original, replacement)); + + Depend special; + Depend custom; + + CHECK(!isSameObject (replacement, special() )); + CHECK(!isSameObject (replacement, custom() )); + }////////////////////////////////////////////////////(End)TEST-Scope + + Sub& nextFetch = genericAccessor(); + CHECK (isSameObject (original, nextFetch)); + CHECK (oID == nextFetch.instanceID_); + + {////////////////////////////////////////////////////TEST-Scope-2 + DependInject::Local otherMock; Sub& replacement = genericAccessor(); uint repID = replacement.instanceID_; @@ -254,11 +239,10 @@ namespace test{ CHECK (!isSameObject (original, subTypeAccess)); CHECK (repID != subTypeAccess.instanceID_); CHECK ( oID != subTypeAccess.instanceID_); - } + }////////////////////////////////////////////////////(End)TEST-Scope-2 - Sub& nextFetch = genericAccessor(); - CHECK (isSameObject (original, nextFetch)); - CHECK (oID == nextFetch.instanceID_); + CHECK (isSameObject (original, genericAccessor())); + CHECK (oID == genericAccessor().instanceID_); } }; diff --git a/tests/library/singleton-subclass-test.cpp b/tests/library/singleton-subclass-test.cpp index 521189a35..567308eea 100644 --- a/tests/library/singleton-subclass-test.cpp +++ b/tests/library/singleton-subclass-test.cpp @@ -32,7 +32,8 @@ #include "lib/util.hpp" #include "test-target-obj.hpp" -#include "lib/depend.hpp" +#include "lib/depend2.hpp" +#include "lib/depend-inject.hpp" #include @@ -65,7 +66,8 @@ namespace test{ Interface () : TestTargetObj(cnt) {} virtual ~Interface() {} - friend class lib::DependencyFactory; + friend class lib::InstanceHolder; + friend class std::default_delete; }; int Interface::cnt = 0; @@ -86,13 +88,12 @@ namespace test{ /***************************************************************//** - * @test specialised variant of the Singleton Factory, for creating - * subclasses (implementation classes) without coupling the - * caller to the concrete class type. - * Expected results: an instance of the subclass is created. + * @test specific dependency-injection setup, to create a singleton + * subclass (implementation class) instance, without coupling + * the caller to the concrete type. + * @remark Expected results: an instance of the subclass is created. * @see lib::Depend - * @see lib::buildSingleton() - * @see lib/dependency-factory.hpp + * @see lib/depend-inject.hpp */ class SingletonSubclass_test : public Test { @@ -106,12 +107,11 @@ namespace test{ Interface::setCountParam(num); - // marker to declare the concrete type to be created - DependencyFactory::InstanceConstructor factoryFunction = buildSingleton(); + // configuration to use the subclass on demand + DependInject::useSingleton(); - // define an instance of the Singleton factory, - // specialised to create the concrete Type passed in - Depend instance (factoryFunction); + // define an instance of the Singleton factory as always... + Depend instance; // Now use the Singleton factory... // Note: we get the Base type @@ -119,6 +119,8 @@ namespace test{ Interface& t2 = instance(); CHECK (isSameObject (t1, t2), "not a Singleton, got two different instances." ); + CHECK ( INSTANCEOF (Impl,&t1)); // got the subclass as expected + CHECK ("Implementation" == t2.identify()); cout << "calling a non-static method on the Singleton-" << t1.identify() << endl @@ -132,11 +134,13 @@ namespace test{ void verify_error_detection () { - VERIFY_ERROR (LIFECYCLE, Depend instance (buildSingleton()) ); - VERIFY_ERROR (LIFECYCLE, Depend instance (buildSingleton()) ); + VERIFY_ERROR (LIFECYCLE, DependInject::useSingleton() ); Depend newFactory; CHECK ( INSTANCEOF (Impl, &newFactory() )); // works as before + +//////////does not compile due to incompatible baseclass +// DependInject::useSingleton(); } }; diff --git a/tests/library/singleton-test.cpp b/tests/library/singleton-test.cpp index 1fd565fb4..4a57c7060 100644 --- a/tests/library/singleton-test.cpp +++ b/tests/library/singleton-test.cpp @@ -31,7 +31,7 @@ #include "lib/util.hpp" #include "test-target-obj.hpp" -#include "lib/depend.hpp" +#include "lib/depend2.hpp" #include @@ -59,7 +59,7 @@ namespace test{ protected: TargetObj () : TestTargetObj(cnt) {} - friend class lib::DependencyFactory; + friend class lib::InstanceHolder; }; int TargetObj::cnt = 0; diff --git a/tests/library/singleton-testmock-test.cpp b/tests/library/singleton-testmock-test.cpp index 8749b7957..90f0cd91d 100644 --- a/tests/library/singleton-testmock-test.cpp +++ b/tests/library/singleton-testmock-test.cpp @@ -27,7 +27,7 @@ #include "lib/test/run.hpp" -#include "lib/depend.hpp" +#include "lib/depend-inject.hpp" #include "lib/util.hpp" #include "lib/format-cout.hpp" @@ -45,14 +45,14 @@ namespace test{ * Client Class normally to be instantiated as Singleton. * But for tests, this class should be replaced by a Mock.... */ - class TestSingletonO + class TestSingO { int callCnt_; Symbol typid_; _Fmt msg_; public: - TestSingletonO(Symbol ty="TestSingletonO") + TestSingO(Symbol ty="TestSingO") : callCnt_(0) , typid_(ty) , msg_("%s::doIt() call=%d\n") @@ -61,7 +61,7 @@ namespace test{ } virtual - ~TestSingletonO() + ~TestSingO() { TRACE (test, "dtor %s", typid_.c()); } @@ -83,17 +83,23 @@ namespace test{ /** * Mock-1 to replace the Client Class... */ - struct Mock_1 : TestSingletonO + struct Mock_1 : TestSingO { - Mock_1() : TestSingletonO("Mock_1") { }; + Mock_1() : TestSingO("Mock_1") { }; }; /** * Mock-2 to replace the Client Class... + * @note no default ctor */ - struct Mock_2 : TestSingletonO + struct Mock_2 : TestSingO { - Mock_2() : TestSingletonO("Mock_2") { }; + int id; + + Mock_2(Literal specialID, int i) + : TestSingO{Symbol (_Fmt{"%s_%d"} % specialID % i)} + , id{i} + { }; }; @@ -112,7 +118,7 @@ namespace test{ * Client Object, then replace it by two different mocks, * and finally restore the original Client Object. * @see lib::Depend - * @see lib::test::Depend4Test + * @see depend-inject.hpp * @see DependencyFactory_test */ class SingletonTestMock_test : public Test @@ -121,28 +127,36 @@ namespace test{ void run (Arg) { - Depend sing; + Depend sing; sing().doIt(); sing().doIt(); CHECK (sing().getCnt() == 2); - Mock_1 mock_1; - TestSingletonO* original = - sing.injectReplacement (&mock_1); - sing().doIt(); - sing().doIt(); - sing().doIt(); - sing().doIt(); - sing().doIt(); - CHECK (sing().getCnt() == 5); + { + // shadow by local Mock instance + DependInject::Local mock_1; + sing().doIt(); + sing().doIt(); + sing().doIt(); + sing().doIt(); + sing().doIt(); + CHECK (sing().getCnt() == 5); + + // shadow again by different local Mock, this time with special ctor call + int instanceID = 0; + DependInject::Local mock_2 ([&]{ return new Mock_2{"Mock", instanceID}; }); + + // NOTE: the ctor call for the Mock really happens delayed... + instanceID = rand() % 10; + sing().doIt(); // ctor invoked on first access + CHECK (sing().getCnt() == 1); + + // can access the Mock for instrumentation + CHECK (instanceID == mock_2->id); + + }// original instance automatically un-shadowed here - Mock_2 mock_2; - sing.injectReplacement (&mock_2); - sing().doIt(); - CHECK (sing().getCnt() == 1); - - sing.injectReplacement (original); // un-shadowing original instance CHECK (sing().getCnt() == 2); sing().doIt(); CHECK (sing().getCnt() == 3); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 85b9a3735..3e2143b80 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -27775,28 +27775,29 @@ - - - + + + + - - + + - - + + - - - + + + - - - - + + + + @@ -27824,6 +27825,54 @@ + + + + + + +

+ die von der alten DependencyFactory abhängen +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -27831,6 +27880,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -27866,6 +27972,8 @@
+ + @@ -27891,6 +27999,7 @@ +