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:
Fischlurch 2018-03-17 17:30:28 +01:00
parent 261049e04d
commit e393d44e92
2 changed files with 107 additions and 7 deletions

View file

@ -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";

View file

@ -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&#xe4;re die &quot;Standard-L&#xf6;sung&quot; f&#xfc;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&#xfc;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&#xfc;r Meyers Singleton nochmal ein Lock generiert"/>
</node>
<node CREATED="1521303775845" ID="ID_1256913997" MODIFIED="1521303814515" TEXT="weil wir unsere Singletons ggfs explizit zerst&#xf6;ren wollen"/>
<node CREATED="1521303816879" ID="ID_1463480909" MODIFIED="1521303842240" TEXT="weil wir eine Closure f&#xfc;r den Konstruktor erzeugen wollen"/>
<node CREATED="1521303847923" ID="ID_917362189" MODIFIED="1521303866596" TEXT="weil wir Abstrakte Typen explizit ausschlie&#xdf;en m&#xfc;ssen"/>
</node>
<node CREATED="1521303869768" ID="ID_712811337" MODIFIED="1521303904421" TEXT="all das w&#xe4;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&#xfc;ckisch">
<icon BUILTIN="flag-pink"/>