DI: implement creating singleton from arbitrary (user provided) closure/functor/lambda
this is quite an ugly feature, but I couldn't come up with any convincing argument *not* to implement it (and its low hanging fruit)
This commit is contained in:
parent
e74576f6b0
commit
d9af3abb0f
4 changed files with 193 additions and 33 deletions
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/depend2.hpp"
|
||||
#include "lib/meta/function.hpp"
|
||||
#include "lib/sync-classlock.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
|
@ -83,11 +84,34 @@ namespace lib {
|
|||
static void
|
||||
useSingleton()
|
||||
{
|
||||
__assert_compatible<SUB>();
|
||||
static InstanceHolder<SUB> singleton;
|
||||
installFactory ([&]()
|
||||
useSingleton ([]{ return new SUB{}; });
|
||||
}
|
||||
|
||||
/** configure dependency-injection for type SRV to manage a subclass singleton,
|
||||
* which is created lazily on demand by invoking the given builder function
|
||||
* @param ctor functor to create a heap allocated instance of subclass
|
||||
* @throws error::Logic (LUMIERA_ERROR_LIFECYCLE) when the default factory has already
|
||||
* been invoked at the point when calling this (re)configuration function.
|
||||
*/
|
||||
template<class FUN>
|
||||
static void
|
||||
useSingleton(FUN&& ctor)
|
||||
{
|
||||
using lib::meta::_Fun;
|
||||
using lib::meta::Strip;
|
||||
|
||||
static_assert (_Fun<FUN>::value, "Need a Lambda or Function object to create a heap allocated instance");
|
||||
|
||||
using Ret = typename _Fun<FUN>::Ret;
|
||||
using Sub = typename Strip<Ret>::TypePlain;
|
||||
|
||||
__assert_compatible<Sub>();
|
||||
static_assert (std::is_pointer<Ret>::value, "Function must yield a pointer to a heap allocated instance");
|
||||
|
||||
static InstanceHolder<Sub> singleton;
|
||||
installFactory ([ctor]()
|
||||
{
|
||||
return singleton.buildInstance();
|
||||
return singleton.buildInstance (ctor);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -167,12 +191,18 @@ namespace lib {
|
|||
|
||||
public:
|
||||
Local()
|
||||
: Local([]{ return new MOC{}; })
|
||||
{ }
|
||||
|
||||
template<class FUN>
|
||||
explicit
|
||||
Local (FUN&& buildInstance)
|
||||
{
|
||||
__assert_compatible<MOC>();
|
||||
temporarilyInstallAlternateFactory (origInstance_, origFactory_
|
||||
,[this]()
|
||||
,[=]()
|
||||
{
|
||||
mock_.reset(new MOC{});
|
||||
mock_.reset (buildInstance());
|
||||
return mock_.get();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,6 +101,13 @@ namespace lib {
|
|||
public:
|
||||
TAR*
|
||||
buildInstance()
|
||||
{
|
||||
return buildInstance ([]{ return new TAR{}; });
|
||||
}
|
||||
|
||||
template<class FUN>
|
||||
TAR*
|
||||
buildInstance(FUN&& ctor)
|
||||
{
|
||||
if (instance_)
|
||||
throw error::Fatal("Attempt to double-create a singleton service. "
|
||||
|
|
@ -108,8 +115,7 @@ namespace lib {
|
|||
"or runtime system is seriously broken"
|
||||
,error::LUMIERA_ERROR_LIFECYCLE);
|
||||
|
||||
// place new instance into embedded buffer
|
||||
instance_.reset (new TAR{});
|
||||
instance_.reset (ctor());
|
||||
return instance_.get();
|
||||
}
|
||||
};
|
||||
|
|
@ -119,7 +125,7 @@ namespace lib {
|
|||
{
|
||||
public:
|
||||
ABS*
|
||||
buildInstance()
|
||||
buildInstance(...)
|
||||
{
|
||||
throw error::Fatal("Attempt to create a singleton instance of an abstract class. "
|
||||
"Application architecture or lifecycle is seriously broken.");
|
||||
|
|
|
|||
|
|
@ -109,10 +109,10 @@ namespace test{
|
|||
verify_Singleton();
|
||||
verify_SubclassSingleton();
|
||||
verify_expose_Service_with_Lifecycle();
|
||||
verify_customFactory();
|
||||
verify_automaticReplacement();
|
||||
verify_customFactory();
|
||||
|
||||
CHECK (7+5+1 == checksum); // singletons stay alive until application shutdown
|
||||
CHECK (9+7+5+1 == checksum); // singletons stay alive until application shutdown
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -204,15 +204,6 @@ namespace test{
|
|||
|
||||
|
||||
|
||||
/** @test instance creation can be preconfigured with a closure */
|
||||
void
|
||||
verify_customFactory()
|
||||
{
|
||||
TODO ("implement arbitrary ctor closure");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test injecting test mocks temporarily */
|
||||
void
|
||||
verify_automaticReplacement()
|
||||
|
|
@ -311,6 +302,90 @@ namespace test{
|
|||
CHECK ((1+5+7)*7 == dumm().probe() );
|
||||
CHECK ((1+5+7) == checksum );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test instance creation can be preconfigured with a closure
|
||||
* Both Singleton and Test-Mock creation can optionally be performed through a
|
||||
* user provided Lambda or Functor. To demonstrate this, we use a `Veryspecial` local class,
|
||||
* which takes an `int&` as constructor parameter -- and we create the actual instance through
|
||||
* a lambda, which happens to capture a local variable by reference.
|
||||
* @warning this can be dangerous; in the example demonstrated here, the created singleton instance
|
||||
* continues to live until termination of the test-suite. After leaving this test function,
|
||||
* it thus holds a dangling reference, pointing into stack memory....
|
||||
*/
|
||||
void
|
||||
verify_customFactory()
|
||||
{
|
||||
CHECK ((1+5+7) == checksum );
|
||||
|
||||
struct Veryspecial
|
||||
: Dummy<9>
|
||||
{
|
||||
Veryspecial(int& ref)
|
||||
: magic_{ref}
|
||||
{ }
|
||||
|
||||
int& magic_;
|
||||
|
||||
virtual int
|
||||
probe() override
|
||||
{
|
||||
return magic_++;
|
||||
}
|
||||
};
|
||||
|
||||
int backdoor = 22;
|
||||
|
||||
DependInject<Dummy<9>>::useSingleton ([&]{ return new Veryspecial{backdoor}; });
|
||||
|
||||
CHECK ((1+5+7) == checksum );
|
||||
CHECK ( 22 == backdoor );
|
||||
|
||||
Depend<Dummy<9>> tricky;
|
||||
CHECK ((1+5+7) == checksum );
|
||||
CHECK (22 == backdoor );
|
||||
|
||||
CHECK (22 == tricky().probe());
|
||||
CHECK (23 == backdoor );
|
||||
CHECK ((1+5+7+9) == checksum ); // Veryspecial Dummy<9> subclass was created on the heap
|
||||
// and will continue to live there until the testsuite terminates
|
||||
backdoor = 41;
|
||||
CHECK (41 == tricky().probe());
|
||||
CHECK (42 == backdoor );
|
||||
|
||||
|
||||
Depend<Dum> dumm;
|
||||
CHECK ((1+5+7+9)*7 == dumm().probe() );
|
||||
|
||||
{////////////////////////////////////////////////////TEST-Scope
|
||||
|
||||
DependInject<Dum>::Local<Dum> insidious ([&]{ return new Veryspecial{backdoor}; });
|
||||
|
||||
CHECK ((1+5+7+9) == checksum );
|
||||
CHECK (not insidious);
|
||||
|
||||
CHECK (42 == dumm().probe() );
|
||||
CHECK (43 == backdoor );
|
||||
CHECK ((1+5+7+9+9) == checksum );
|
||||
|
||||
CHECK (isSameObject (dumm(), *insidious));
|
||||
|
||||
CHECK (43 == tricky().probe());
|
||||
CHECK (44 == backdoor );
|
||||
|
||||
backdoor = -1;
|
||||
CHECK (-1 == dumm().probe() );
|
||||
CHECK ( 0 == backdoor );
|
||||
|
||||
CHECK ((1+5+7+9+9) == checksum );
|
||||
}////////////////////////////////////////////////////(End)TEST-Scope
|
||||
|
||||
CHECK ((1+5+7+9) == checksum );
|
||||
CHECK ((1+5+7+9)*7 == dumm().probe() );
|
||||
CHECK ( 0 == tricky().probe());
|
||||
CHECK (+1 == backdoor );
|
||||
} // NOTE: Veryspecial holds a dangling reference into stack memory from now on!
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26792,10 +26792,16 @@
|
|||
</node>
|
||||
<node CREATED="1521207954859" ID="ID_631339002" MODIFIED="1521418307590" TEXT="Analyse">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1521208242178" ID="ID_539882602" MODIFIED="1521208246821" TEXT="erforderliche Mechanismen">
|
||||
<node CREATED="1521208254376" ID="ID_1933406851" MODIFIED="1521208256603" TEXT="Singleton">
|
||||
<node CREATED="1521208339124" ID="ID_287433738" MODIFIED="1521209063045" TEXT="Closure über konkreten Ctor"/>
|
||||
<node CREATED="1521208339124" ID="ID_826329078" MODIFIED="1521387972747" TEXT="(optional)Closure mit speziellen Argumenten"/>
|
||||
<node CREATED="1521208242178" ID="ID_539882602" MODIFIED="1521696589693" TEXT="erforderliche Mechanismen">
|
||||
<icon BUILTIN="info"/>
|
||||
<node COLOR="#338800" CREATED="1521208254376" ID="ID_1933406851" MODIFIED="1521696578693" TEXT="Singleton">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1521208339124" ID="ID_287433738" MODIFIED="1521696567487" TEXT="Closure über konkreten Ctor">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1521208339124" ID="ID_826329078" MODIFIED="1521696572039" TEXT="(optional)Closure mit speziellen Argumenten">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1521209708336" ID="ID_1667188609" MODIFIED="1521332921903" TEXT="Storage für UnterBla-Instanz bereitstellen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
|
|
@ -26861,8 +26867,11 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1521208261887" ID="ID_384054163" MODIFIED="1521208264827" TEXT="Local">
|
||||
<node CREATED="1521209044203" ID="ID_1994153994" MODIFIED="1521409010023" TEXT="(optional)Closure mit speziellen Argumenten "/>
|
||||
<node COLOR="#338800" CREATED="1521208261887" ID="ID_384054163" MODIFIED="1521696582060" TEXT="Local">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1521209044203" ID="ID_1994153994" MODIFIED="1521696576989" TEXT="(optional)Closure mit speziellen Argumenten ">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1521209708336" ID="ID_1870723126" MODIFIED="1521411844636" TEXT="Storage für UnterBla-Instanz bereitstellen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
|
|
@ -27049,7 +27058,7 @@
|
|||
<node CREATED="1521242824239" ID="ID_1013563756" MODIFIED="1521242834313" TEXT="inline-wegwerf-Objekt"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1521240053852" FOLDED="true" ID="ID_666003564" MODIFIED="1521418511493" STYLE="fork" TEXT="Detail-Fragen">
|
||||
<node CREATED="1521240053852" FOLDED="true" ID="ID_666003564" MODIFIED="1521696619000" STYLE="fork" TEXT="Detail-Fragen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#990000" CREATED="1521169099047" ID="ID_1183392158" MODIFIED="1521418281285" TEXT="DependencyFactory-Rahmenklasse">
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
|
|
@ -27219,8 +27228,8 @@
|
|||
<node COLOR="#338800" CREATED="1521418571594" ID="ID_1432227459" MODIFIED="1521688407751" TEXT="DependInject<SRV> ersetzt dependency-factory.hpp">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521418598463" ID="ID_237484313" MODIFIED="1521418617389" TEXT="Objekterzeugung erweitern">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1521418598463" ID="ID_237484313" MODIFIED="1521696529892" TEXT="Objekterzeugung erweitern">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#990000" CREATED="1521418618052" ID="ID_1253147848" MODIFIED="1521688468798" TEXT="auf beliebige Argumente">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
<node CREATED="1521688471643" ID="ID_1037697154" MODIFIED="1521688504558" TEXT="gefährlich wenn Objekterzeugung lazy">
|
||||
|
|
@ -27231,8 +27240,42 @@
|
|||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521418627130" ID="ID_206705145" MODIFIED="1521418639801" TEXT="auf allgemeine Closures">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1521418627130" ID="ID_206705145" MODIFIED="1521696521621" TEXT="auf allgemeine Closures">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1521696315169" ID="ID_914761003" MODIFIED="1521696326988" TEXT="ob das wohl geht...."/>
|
||||
<node CREATED="1521696477035" ID="ID_1232450528" MODIFIED="1521696510682" TEXT="Verdammter Mist! Wird die Closure nun kopiert oder geMOVEd?"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1521696673440" ID="ID_1408713510" MODIFIED="1521696682160" TEXT="compile-time Checks mal verifizieren">
|
||||
<icon BUILTIN="flag-pink"/>
|
||||
</node>
|
||||
<node CREATED="1521696334383" ID="ID_1627167666" MODIFIED="1521696684626" TEXT="gefällt mir nicht wirklich">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
denn nun wird das "singleton" schon ziemlich gehaltlos,
|
||||
</p>
|
||||
<p>
|
||||
und es ist einigermaßen undurchsichtig, wo nun die Instanz erzeugt wird.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Allerdings gibt es auch kein stichhaltiges Argument, dieses Feature<i> nicht</i> zu implementieren.
|
||||
</p>
|
||||
<p>
|
||||
Es ist halt einfach nahheliegend, daß man mal eine Subklasse mit abweichenden Parametern
|
||||
</p>
|
||||
<p>
|
||||
konstruieren wollen könnte, und es ist von der Implementierung her "quasi geschenkt".
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="smily_bad"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521433938944" ID="ID_546086969" MODIFIED="1521433946432" TEXT="double-checked-locking">
|
||||
|
|
@ -27286,8 +27329,14 @@
|
|||
<node COLOR="#338800" CREATED="1521433807714" ID="ID_655174082" MODIFIED="1521433832327" TEXT="Assertions explizit machen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521433833942" ID="ID_841958834" MODIFIED="1521433843486" TEXT="Factory-Closure abdecken">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1521433833942" ID="ID_841958834" MODIFIED="1521696534947" TEXT="Factory-Closure abdecken">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1521696537522" ID="ID_1639668362" MODIFIED="1521696552680" TEXT="für subclass-Singleton">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1521696547161" ID="ID_413219021" MODIFIED="1521696551785" TEXT="für Test-Mock">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1521433852660" ID="ID_1581010755" MODIFIED="1521433857543" TEXT="läuft">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue