DI: replace Meyers Singleton by an explicitly managed buffer
Meyers Singleton is elegant and fast and considered the default solution However... - we want an "instance" pointer that can be rebound and reset, and thus we are forced to use an explicit Mutex and an atomic variable. And the situation is such that the optimiser can not detect/verify this usage and thus generates a spurious additional lock for Meyers Singleton - we want the option to destroy our singletons explicitly - we need to create an abstracted closure for the ctor invocation - we need a compiletime-branch to exclude code generation for invoking the ctor of an abstract baseclass or interface All those points would be somehow manageable, but would counterfeit the simplicity of Meyers Singleton
This commit is contained in:
parent
261049e04d
commit
e393d44e92
2 changed files with 107 additions and 7 deletions
|
|
@ -48,12 +48,14 @@ typedef unsigned int uint;
|
|||
#include "lib/format-cout.hpp"
|
||||
#include "lib/depend.hpp"
|
||||
#include "lib/depend2.hpp"
|
||||
#include "lib/meta/util.hpp"
|
||||
//#include "lib/meta/util.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
#define SHOW_TYPE(_TY_) \
|
||||
|
|
@ -62,9 +64,64 @@ typedef unsigned int uint;
|
|||
cout << "Probe " << STRINGIFY(_XX_) << " ? = " << _XX_ <<endl;
|
||||
|
||||
|
||||
using lib::ClassLock;
|
||||
namespace error = lumiera::error;
|
||||
|
||||
using lib::ClassLock;
|
||||
using lib::meta::enable_if;
|
||||
|
||||
|
||||
|
||||
template<typename TAR, typename SEL =void>
|
||||
class InstanceHolder
|
||||
: boost::noncopyable
|
||||
{
|
||||
/** storage for the service instance */
|
||||
char buff_[sizeof(TAR)];
|
||||
bool alive_ = false;
|
||||
|
||||
|
||||
public:
|
||||
~InstanceHolder()
|
||||
{
|
||||
if (alive_)
|
||||
try {
|
||||
alive_ = false;
|
||||
reinterpret_cast<TAR&> (buff_). ~TAR();
|
||||
}
|
||||
catch(...)
|
||||
{ // no logging since we might be in static shutdown
|
||||
lumiera_error(); // reset errorflag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TAR*
|
||||
buildInstance()
|
||||
{
|
||||
if (alive_)
|
||||
throw error::Fatal("Attempt to double-create a singleton service. "
|
||||
"Either the application logic, or the compiler "
|
||||
"or runtime system is seriously broken"
|
||||
,error::LUMIERA_ERROR_LIFECYCLE);
|
||||
|
||||
// place new instance into embedded buffer
|
||||
TAR* newInstance = new(&buff_) TAR ();
|
||||
alive_ = true;
|
||||
return newInstance;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ABS>
|
||||
class InstanceHolder<ABS, enable_if<std::is_abstract<ABS>>>
|
||||
{
|
||||
public:
|
||||
ABS*
|
||||
buildInstance()
|
||||
{
|
||||
throw error::Fatal("Attempt to create a singleton instance of an abstract class. "
|
||||
"Application architecture or lifecycle is seriously broken.");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
@ -74,14 +131,16 @@ class DependInject;
|
|||
template<class SRV>
|
||||
class Depend
|
||||
{
|
||||
public:
|
||||
using Factory = std::function<SRV*()>;
|
||||
|
||||
static SRV* instance;
|
||||
static Factory factory;
|
||||
|
||||
static InstanceHolder<SRV> singleton;
|
||||
|
||||
friend class DependInject<SRV>;
|
||||
|
||||
public:
|
||||
SRV&
|
||||
operator() ()
|
||||
{
|
||||
|
|
@ -101,8 +160,7 @@ class Depend
|
|||
{
|
||||
if (!factory)
|
||||
{
|
||||
static SRV singleton{};
|
||||
instance = &singleton;
|
||||
instance = singleton.buildInstance();
|
||||
factory = disabledFactory;
|
||||
}
|
||||
else
|
||||
|
|
@ -125,17 +183,29 @@ SRV* Depend<SRV>::instance;
|
|||
template<class SRV>
|
||||
typename Depend<SRV>::Factory Depend<SRV>::factory;
|
||||
|
||||
template<class SRV>
|
||||
InstanceHolder<SRV> Depend<SRV>::singleton;
|
||||
|
||||
|
||||
|
||||
struct Dum
|
||||
: boost::noncopyable
|
||||
{
|
||||
virtual ~Dum() { }
|
||||
virtual int probe() =0;
|
||||
};
|
||||
|
||||
|
||||
int checksum = 0;
|
||||
|
||||
template<int N>
|
||||
struct Dummy
|
||||
: boost::noncopyable
|
||||
: Dum
|
||||
{
|
||||
Dummy() { checksum += N; }
|
||||
~Dummy() { checksum -= N; }
|
||||
|
||||
int
|
||||
virtual int
|
||||
probe()
|
||||
{
|
||||
return N * checksum;
|
||||
|
|
@ -161,6 +231,10 @@ main (int, char**)
|
|||
SHOW_EXPR( dep12().probe() );
|
||||
SHOW_EXPR( checksum );
|
||||
|
||||
Depend<Dum> dumm;
|
||||
Depend<Dum>::factory = [](){ return nullptr; };
|
||||
SHOW_EXPR( dumm().probe() );
|
||||
|
||||
|
||||
cout << "\n.gulp.\n";
|
||||
|
||||
|
|
|
|||
|
|
@ -26995,13 +26995,39 @@
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521254014422" ID="ID_961824675" MODIFIED="1521254035254" TEXT="Meyers Singleton oder explizit ausprogrammieren?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1521303577633" ID="ID_1665490706" MODIFIED="1521303615967" TEXT="das wäre die "Standard-Lösung" für Singletons"/>
|
||||
<node CREATED="1521303616675" ID="ID_135579041" MODIFIED="1521303721048" TEXT="hier aber nicht sinnvoll...">
|
||||
<node CREATED="1521303634705" ID="ID_433169847" MODIFIED="1521303737486" TEXT="weil wir explizit ein Lock halten müssen">
|
||||
<node CREATED="1521303738410" ID="ID_213625497" MODIFIED="1521303748316" TEXT="um den Instanz-Pointer und die Factory zu managen"/>
|
||||
<node CREATED="1521303749169" ID="ID_762346402" MODIFIED="1521303772913" TEXT="und der Compiler für Meyers Singleton nochmal ein Lock generiert"/>
|
||||
</node>
|
||||
<node CREATED="1521303775845" ID="ID_1256913997" MODIFIED="1521303814515" TEXT="weil wir unsere Singletons ggfs explizit zerstören wollen"/>
|
||||
<node CREATED="1521303816879" ID="ID_1463480909" MODIFIED="1521303842240" TEXT="weil wir eine Closure für den Konstruktor erzeugen wollen"/>
|
||||
<node CREATED="1521303847923" ID="ID_917362189" MODIFIED="1521303866596" TEXT="weil wir Abstrakte Typen explizit ausschließen müssen"/>
|
||||
</node>
|
||||
<node CREATED="1521303869768" ID="ID_712811337" MODIFIED="1521303904421" TEXT="all das wäre auch mit Meyers Singleton irgendwie hinzubekommen....">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...aber dann eben nicht mehr <i>elegant.</i>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1521303905491" ID="ID_1749674332" MODIFIED="1521303916944" TEXT="Fazit: explizit ist besser">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521253797733" ID="ID_1640990866" MODIFIED="1521253802565" TEXT="Fehlerbehandlung">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521253803963" ID="ID_100415207" MODIFIED="1521253827633" TEXT="Installation wenn Factory bereits genutzt wurde">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521253831776" ID="ID_1332033733" MODIFIED="1521253852814" TEXT="Zugriff auf Service bevor her hochgefahren wurde">
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521253831776" ID="ID_1332033733" MODIFIED="1521303545433" TEXT="Zugriff auf Service bevor er hochgefahren wurde">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1521253858692" ID="ID_442301762" MODIFIED="1521253866011" TEXT="tückisch">
|
||||
<icon BUILTIN="flag-pink"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue