DI: rework dependency-injection configuration in terms of the new DependencyFactory

why is this so damn hard to get right?
This commit is contained in:
Fischlurch 2018-03-30 05:56:53 +02:00
parent 88e37d5681
commit b3d18c1a74
6 changed files with 212 additions and 166 deletions

View file

@ -160,6 +160,7 @@ namespace lib {
public:
/** configure dependency-injection for type SRV to build a subclass singleton
* @note actually a delegation to `Depend<SUB>` is installed into `Depend<SRV>`
* @tparam SUB concrete subclass type to build on demand when invoking `Depend<SRV>`
* @throws error::Logic (LUMIERA_ERROR_LIFECYCLE) when the default factory has already
* been invoked at the point when calling this (re)configuration function.
@ -168,11 +169,14 @@ namespace lib {
static void
useSingleton()
{
installFactory ([]{ return & Depend<SUB>{}(); });
__assert_compatible<SUB>();
installFactory<SUB>();
}
/** configure dependency-injection for type SRV to manage a subclass singleton,
* which is created lazily on demand by invoking the given builder function
* @note actual Subclass type is determined from the given functor and then
* a delegation to `Depend<Subclass>` is installed into `Depend<SRV>`
* @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.
@ -181,11 +185,11 @@ namespace lib {
static void
useSingleton(FUN&& ctor)
{
using Fun = typename SubclassFactory<FUN>::Fun;
using Sub = typename SubclassFactory<FUN>::Sub;
__assert_compatible<Sub>();
using Fun = typename SubclassFactoryType<FUN>::Fun;
using Sub = typename SubclassFactoryType<FUN>::Sub;
installFactory (buildCustomSingleton<Sub,Fun> (forward<FUN> (ctor)));
__assert_compatible<Sub>();
installFactory<Sub,Fun> (forward<FUN> (ctor));
}
@ -258,7 +262,7 @@ namespace lib {
std::unique_ptr<MOC> mock_;
SRV* origInstance_;
Factory origFactory_;
DependencyFactory<SRV> origFactory_;
public:
Local()
@ -270,7 +274,7 @@ namespace lib {
Local (FUN&& buildInstance)
{
__assert_compatible<MOC>();
__assert_compatible<typename SubclassFactory<FUN>::Sub>();
__assert_compatible<typename SubclassFactoryType<FUN>::Sub>();
temporarilyInstallAlternateFactory (origInstance_, origFactory_
,[=]()
@ -281,7 +285,7 @@ namespace lib {
}
~Local()
{
restoreOriginalFactory (origInstance_, origFactory_);
restoreOriginalFactory (origInstance_, move(origFactory_));
}
explicit
@ -322,8 +326,17 @@ namespace lib {
,"Installed implementation class must be compatible to the interface.");
}
static void
__ensure_pristine()
{
if (Depend<SRV>::instance)
throw error::Logic("Attempt to reconfigure dependency injection after the fact. "
"The previously installed factory (typically Singleton) was already used."
, error::LUMIERA_ERROR_LIFECYCLE);
}
template<typename FUN>
struct SubclassFactory
struct SubclassFactoryType
{
static_assert (meta::_Fun<FUN>(),
"Need a Lambda or Function object to create a heap allocated instance");
@ -337,49 +350,53 @@ namespace lib {
};
template<class SUB, typename FUN>
static void
installFactory (Factory&& otherFac)
installFactory (FUN&& ctor)
{
Lock guard;
if (Depend<SRV>::instance)
throw error::Logic("Attempt to reconfigure dependency injection after the fact. "
"The previously installed factory (typically Singleton) was already used."
, error::LUMIERA_ERROR_LIFECYCLE);
Depend<SRV>::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<class SUB, class FUN>
static Factory
buildCustomSingleton (FUN&& ctor)
{
static std::unique_ptr<SUB> singleton;
return ([ctor]() // copy of ctor in the closure
{
singleton.reset (ctor());
return singleton.get();
});
if (std::is_same<SRV,SUB>())
{
__ensure_pristine();
Depend<SRV>::factory.defineCreatorAndManage (forward<FUN> (ctor));
}
else
{
__ensure_pristine();
Depend<SRV>::factory.defineCreator ([]{ return & Depend<SUB>{}(); });
DependInject<SUB>::useSingleton (forward<FUN> (ctor));
} // delegate actual instance creation to Depend<SUB>
}
template<class SUB>
static void
temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, Factory&& newFac)
installFactory ()
{
if (not std::is_same<SRV,SUB>())
{
Lock guard;
__ensure_pristine();
Depend<SRV>::factory.defineCreator ([]{ return & Depend<SUB>{}(); });
}
}
template<typename FUN>
static void
temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, FUN&& newFac)
{
Lock guard;
stashFac = move(Depend<SRV>::factory);
stashFac = move(Depend<SRV>::factory); //////////////////////////////////////TICKET #1059 : GCC-4.9 stubbornly picks the copy assignment
stashInstance = Depend<SRV>::instance;
Depend<SRV>::factory = move(newFac);
Depend<SRV>::factory.defineCreator (forward<FUN>(newFac)); //////////////////////////////////////TICKET #1059 : GCC-4.9 stubbornly picks the copy assignment
Depend<SRV>::instance = nullptr;
}
static void
restoreOriginalFactory (SRV*& stashInstance, Factory& stashFac)
restoreOriginalFactory (SRV*& stashInstance, Factory&& stashFac)
{
Lock guard;
Depend<SRV>::factory = move(stashFac);
Depend<SRV>::factory = move(stashFac); //////////////////////////////////////TICKET #1059 : GCC-4.9 stubbornly picks the copy assignment
Depend<SRV>::instance = stashInstance;
}
@ -392,7 +409,7 @@ namespace lib {
"but another instance has already been dependency-injected."
, error::LUMIERA_ERROR_LIFECYCLE);
Depend<SRV>::instance = &newInstance;
Depend<SRV>::factory = Depend<SRV>::disabledFactory;
Depend<SRV>::factory.disable();
}
static void
@ -400,7 +417,7 @@ namespace lib {
{
Lock guard;
Depend<SRV>::instance = nullptr;
Depend<SRV>::factory = Depend<SRV>::disabledFactory;
Depend<SRV>::factory.disable();
}
};

View file

@ -110,7 +110,7 @@ namespace lib {
*/
template<class OBJ>
class DependencyFactory
: util::MoveOnly
// : util::MoveAssign //////////////////////////////////////////////TICKET #1059 : GCC-4.9 stubbornly picks the copy assignment
{
using Creator = std::function<OBJ*()>;
using Deleter = std::function<void()>;
@ -126,11 +126,6 @@ namespace lib {
deleter_();
}
explicit operator bool() const
{
return bool(creator_);
}
OBJ*
operator() ()
{
@ -138,15 +133,37 @@ namespace lib {
: buildAndManage();
}
OBJ*
buildAndManage()
template<typename FUN>
void
defineCreator (FUN&& ctor)
{
OBJ* obj = buildInstance<OBJ>();
atDestruction ([obj]{ delete obj; });
creator_ = std::forward<FUN> (ctor);
}
template<typename FUN>
DependencyFactory&
void
defineCreatorAndManage (FUN&& ctor)
{
creator_ = [this,ctor]
{
OBJ* obj = ctor();
atDestruction ([obj]{ delete obj; });
return obj;
};
}
void
disable()
{
creator_ = []() -> OBJ*
{
throw error::Fatal("Service not available at this point of the Application Lifecycle"
,error::LUMIERA_ERROR_LIFECYCLE);
};
}
template<typename FUN>
void
atDestruction (FUN&& additionalAction)
{
if (deleter_)
@ -160,10 +177,24 @@ namespace lib {
}
else
deleter_ = std::forward<FUN> (additionalAction);
return *this;
}
private:
OBJ*
buildAndManage()
{
OBJ* obj = buildInstance<OBJ>();
atDestruction ([obj]{ delete obj; });
return obj;
}
template<class TAR>
static meta::enable_if<std::is_constructible<TAR>,
TAR* >
buildInstance()
{
return new TAR;
}
template<class ABS>
static meta::enable_if<std::is_abstract<ABS>,
ABS* >
@ -172,21 +203,14 @@ namespace lib {
throw error::Fatal("Attempt to create a singleton instance of an abstract class. "
"Application architecture or lifecycle is seriously broken.");
}
template<class TAR>
static meta::disable_if<std::is_abstract<TAR>,
TAR* >
template<class ABS>
static meta::disable_if<std::__or_<std::is_abstract<ABS>,std::is_constructible<ABS>>,
ABS* >
buildInstance()
{
return new TAR;
throw error::Fatal("Desired singleton class is not default constructible. "
"Application architecture or lifecycle is seriously broken.");
}
static void
destroy (OBJ* obj)
{
if (obj)
delete obj;
}
};
@ -214,11 +238,11 @@ namespace lib {
template<class SRV>
class Depend
{
using Factory = std::function<SRV*()>;
using Factory = DependencyFactory<SRV>;
using Lock = ClassLock<SRV, NonrecursiveLock_NoWait>;
static std::atomic<SRV*> instance;
static Factory factory;
static DependencyFactory<SRV> factory;
friend class DependInject<SRV>;
@ -240,75 +264,15 @@ namespace lib {
object = instance.load (std::memory_order_relaxed);
if (!object)
{
if (!factory)
{
object = buildInstance<SRV>();
deleter = []{
destroy (instance);
instance = nullptr;
};
}
else
object = factory();
factory = disabledFactory;
object = factory();
factory.disable();
factory.atDestruction([]{ instance = nullptr; });
}
instance.store (object, std::memory_order_release);
}
ENSURE (object);
return *object;
}
private:
/** @internal preconfigured factory to block any (further) on-demand instance creation */
static SRV*
disabledFactory()
{
throw error::Fatal("Service not available at this point of the Application Lifecycle"
,error::LUMIERA_ERROR_LIFECYCLE);
}
template<class ABS>
static meta::enable_if<std::is_abstract<ABS>,
ABS* >
buildInstance()
{
throw error::Fatal("Attempt to create a singleton instance of an abstract class. "
"Application architecture or lifecycle is seriously broken.");
}
template<class TAR>
static meta::disable_if<std::is_abstract<TAR>,
TAR* >
buildInstance()
{
return new TAR;
}
static void
destroy (SRV* obj)
{
if (obj)
delete obj;
}
class Deleter
{
std::function<void(void)> cleanUp_;
public:
~Deleter()
{
if (cleanUp_)
cleanUp_();
}
template<typename DEL>
void operator= (DEL&& fun)
{
cleanUp_ = std::forward<DEL> (fun);
}
};
static Deleter deleter;
};
@ -319,10 +283,7 @@ namespace lib {
std::atomic<SRV*> Depend<SRV>::instance;
template<class SRV>
typename Depend<SRV>::Factory Depend<SRV>::factory;
template<class SRV>
typename Depend<SRV>::Deleter Depend<SRV>::deleter;
DependencyFactory<SRV> Depend<SRV>::factory;

View file

@ -58,12 +58,26 @@ namespace util {
class MoveOnly
{
protected:
~MoveOnly() = default;
MoveOnly() = default;
MoveOnly (MoveOnly&&) = default;
MoveOnly (MoveOnly const&) = delete;
MoveOnly& operator= (MoveOnly&&) = delete;
MoveOnly& operator= (MoveOnly const&) = delete;
~MoveOnly() = default;
MoveOnly() = default;
MoveOnly (MoveOnly&&) = default;
MoveOnly (MoveOnly const&) = delete;
MoveOnly& operator= (MoveOnly&&) = delete;
MoveOnly& operator= (MoveOnly const&) = delete;
};
/**
* Types marked with this mix-in may be moved and move-assigned
*/
class MoveAssign
{
protected:
~MoveAssign() = default;
MoveAssign() = default;
MoveAssign (MoveAssign&&) = default;
MoveAssign (MoveAssign const&) = delete;
MoveAssign& operator= (MoveAssign&&) = default;
MoveAssign& operator= (MoveAssign const&) = delete;
};
/**
@ -77,12 +91,12 @@ namespace util {
class Cloneable
{
protected:
~Cloneable() = default;
Cloneable() = default;
Cloneable (Cloneable&&) = default;
Cloneable (Cloneable const&) = default;
Cloneable& operator= (Cloneable&&) = delete;
Cloneable& operator= (Cloneable const&) = delete;
~Cloneable() = default;
Cloneable() = default;
Cloneable (Cloneable&&) = default;
Cloneable (Cloneable const&) = default;
Cloneable& operator= (Cloneable&&) = delete;
Cloneable& operator= (Cloneable const&) = delete;
};
/**

View file

@ -66,7 +66,7 @@ namespace test{
Interface () : TestTargetObj(cnt) {}
virtual ~Interface() {}
friend class lib::Depend<Interface>;
friend class lib::DependencyFactory<Interface>;
};
int Interface::cnt = 0;

View file

@ -59,7 +59,7 @@ namespace test{
protected:
TargetObj () : TestTargetObj(cnt) {}
friend class lib::Depend<TargetObj>;
friend class lib::DependencyFactory<TargetObj>;
};
int TargetObj::cnt = 0;

View file

@ -27772,8 +27772,8 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1522200092935" ID="ID_1314237888" MODIFIED="1522200242915" TEXT="Deleter-Behandlung">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1522200092935" ID="ID_1314237888" MODIFIED="1522382097329" TEXT="Deleter-Behandlung">
<icon BUILTIN="pencil"/>
<node CREATED="1522200102534" ID="ID_284894079" MODIFIED="1522200105025" TEXT="bisher">
<node CREATED="1522200106373" ID="ID_1981188993" MODIFIED="1522200226028" TEXT="was die Factory zur&#xfc;ckliefert, wird nicht gemanaged">
<icon BUILTIN="broken-line"/>
@ -27787,35 +27787,89 @@
</node>
<node CREATED="1522200147696" ID="ID_929047067" MODIFIED="1522200270431" TEXT="gew&#xfc;nscht">
<icon BUILTIN="yes"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1522200152927" ID="ID_326569334" MODIFIED="1522200261233" TEXT="keine Memory-leaks">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1522200152927" ID="ID_326569334" MODIFIED="1522382081602" TEXT="keine Memory-leaks">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1522200167861" ID="ID_908074030" MODIFIED="1522200262177" TEXT="alles soll zerst&#xf6;rt werden">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1522200167861" ID="ID_908074030" MODIFIED="1522382083234" TEXT="alles soll zerst&#xf6;rt werden">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1522200189706" ID="ID_302642427" MODIFIED="1522200262864" TEXT="m&#xf6;glichst alle instance-Pointer zur&#xfc;cksetzen">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1522200189706" ID="ID_302642427" MODIFIED="1522382084962" TEXT="m&#xf6;glichst alle instance-Pointer zur&#xfc;cksetzen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1522200249626" ID="ID_215336836" MODIFIED="1522200263456" TEXT="Freunschafts-f&#xe4;hig">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1522200249626" ID="ID_215336836" MODIFIED="1522382087401" TEXT="Freunschafts-f&#xe4;hig">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1522334796724" ID="ID_1080992734" MODIFIED="1522334873818" TEXT="Funktoren zusammenf&#xfc;hren">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1522334843062" ID="ID_1526236017" MODIFIED="1522334870859" TEXT="DependencyFactory f&#xfc;r">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1522334796724" ID="ID_1080992734" MODIFIED="1522382015413" TEXT="Funktoren zusammenf&#xfc;hren">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1522334843062" ID="ID_1526236017" MODIFIED="1522382043115" TEXT="DependencyFactory f&#xfc;r">
<icon BUILTIN="button_ok"/>
<node CREATED="1522334805635" ID="ID_315782435" MODIFIED="1522334817813" TEXT="Erzeugungs-Funktor"/>
<node CREATED="1522334818473" ID="ID_750129060" MODIFIED="1522334821036" TEXT="Deleter"/>
<node CREATED="1522334822001" ID="ID_644059909" MODIFIED="1522334935421" TEXT="automatisches Instanz-Management"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1522334859291" ID="ID_1496044958" MODIFIED="1522334866755" TEXT="einfach konfigurierbar">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1522334896942" ID="ID_1215786079" MODIFIED="1522334902129" TEXT="default-Fall"/>
<node CREATED="1522334903741" ID="ID_1875541619" MODIFIED="1522334911776" TEXT="abstrakte Basisklasse"/>
<node CREATED="1522334912412" ID="ID_1391948557" MODIFIED="1522334916463" TEXT="custom-Funktor"/>
<node CREATED="1522334917035" ID="ID_259163474" MODIFIED="1522334922006" TEXT="custom-Deleter"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1522334859291" ID="ID_1496044958" MODIFIED="1522382005045" TEXT="korrekt handhaben">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1522334896942" ID="ID_1215786079" MODIFIED="1522382047552" TEXT="default-Fall">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1522334903741" ID="ID_1875541619" MODIFIED="1522382049855" TEXT="abstrakte Basisklasse">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1522381984647" ID="ID_136231234" MODIFIED="1522382051295" TEXT="Klasse ohne Default-ctor">
<icon BUILTIN="flag-pink"/>
</node>
<node COLOR="#338800" CREATED="1522334912412" ID="ID_1391948557" MODIFIED="1522382053286" TEXT="custom-Funktor">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1522334917035" ID="ID_259163474" MODIFIED="1522382054565" TEXT="custom-Deleter">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1522367884794" ID="ID_42634215" MODIFIED="1522381896407" TEXT="Analyse der Schritte">
<icon BUILTIN="idea"/>
<node CREATED="1522367595914" ID="ID_615466863" MODIFIED="1522367600141" TEXT="Use-cases">
<node CREATED="1522367601081" ID="ID_1594593457" MODIFIED="1522367603813" TEXT="default">
<node CREATED="1522367513966" ID="ID_18197809" MODIFIED="1522381907638" TEXT="nominelle Target-Klasse">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1522367550688" ID="ID_1892572153" MODIFIED="1522381910426" TEXT="Objekt per default ctor erzeugen">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1522367555520" ID="ID_1067588123" MODIFIED="1522381912781" TEXT="instanz managen">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1522367604945" ID="ID_681132036" MODIFIED="1522367617123" TEXT="Subclass">
<node CREATED="1522367701660" ID="ID_381217567" MODIFIED="1522381919492" TEXT="Subklasse feststellen">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1522367650099" ID="ID_887791148" MODIFIED="1522381923778" TEXT="nur delegieren an Depend&lt;SUB&gt;">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1522367617999" ID="ID_1310241568" MODIFIED="1522367623762" TEXT="custom-Functor">
<node CREATED="1522367712338" ID="ID_1169276081" MODIFIED="1522381927169" TEXT="Functor speichern">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1522367731959" ID="ID_1766531478" MODIFIED="1522381929444" TEXT="Objekt per Funktor erzeugen">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1522367749229" ID="ID_1580519177" MODIFIED="1522381931877" TEXT="instanz managen">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node CREATED="1522367902872" ID="ID_272084666" MODIFIED="1522367919009" TEXT="grunds&#xe4;tzlich immer">
<node CREATED="1522367561111" ID="ID_604859300" MODIFIED="1522381935331" TEXT="instanz-Ptr NULLen">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1522367573173" ID="ID_1825315724" MODIFIED="1522381938038" TEXT="Factory sperren">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
</node>
</node>