This is a tricky problem an an immediate consequence of the dynamic configuration favoured by this design. We avoid a centralised configuration and thus there are no automatic rules to enforce consistency. It would thus be possible to start using a dependency in singleton style, but to switch to service style later, after the fact. An attempt was made to prevent such a mismatch by static initialisiation; basically the presence of any Depend<SRV>::ServiceInstance<X> would disable any usage of Depend<SRV> in singleton style. However, such a mechanism was found to be fragile at best. It seems more apropriate just to fail when establishing a ServiceInstance on a dependency already actively in use (and to lock usage after destroying the ServiceInstance). This issue is considered rather an architectural one, which can not be solved by any mechanism at implementation level ever
374 lines
9.7 KiB
C++
374 lines
9.7 KiB
C++
/* try.cpp - for trying out some language features....
|
|
* scons will create the binary bin/try
|
|
*
|
|
*/
|
|
|
|
// 8/07 - how to control NOBUG??
|
|
// execute with NOBUG_LOG='ttt:TRACE' bin/try
|
|
// 1/08 - working out a static initialisation problem for Visitor (Tag creation)
|
|
// 1/08 - check 64bit longs
|
|
// 4/08 - comparison operators on shared_ptr<Asset>
|
|
// 4/08 - conversions on the value_type used for boost::any
|
|
// 5/08 - how to guard a downcasting access, so it is compiled in only if the involved types are convertible
|
|
// 7/08 - combining partial specialisation and subclasses
|
|
// 10/8 - abusing the STL containers to hold noncopyable values
|
|
// 6/09 - investigating how to build a mixin template providing an operator bool()
|
|
// 12/9 - tracking down a strange "warning: type qualifiers ignored on function return type"
|
|
// 1/10 - can we determine at compile time the presence of a certain function (for duck-typing)?
|
|
// 4/10 - pretty printing STL containers with python enabled GDB?
|
|
// 1/11 - exploring numeric limits
|
|
// 1/11 - integer floor and wrap operation(s)
|
|
// 1/11 - how to fetch the path of the own executable -- at least under Linux?
|
|
// 10/11 - simple demo using a pointer and a struct
|
|
// 11/11 - using the boost random number generator(s)
|
|
// 12/11 - how to detect if string conversion is possible?
|
|
// 1/12 - is partial application of member functions possible?
|
|
// 5/14 - c++11 transition: detect empty function object
|
|
// 7/14 - c++11 transition: std hash function vs. boost hash
|
|
// 9/14 - variadic templates and perfect forwarding
|
|
// 11/14 - pointer to member functions and name mangling
|
|
// 8/15 - Segfault when loading into GDB (on Debian/Jessie 64bit
|
|
// 8/15 - generalising the Variant::Visitor
|
|
// 1/16 - generic to-string conversion for ostream
|
|
// 1/16 - build tuple from runtime-typed variant container
|
|
// 3/17 - generic function signature traits, including support for Lambdas
|
|
// 9/17 - manipulate variadic templates to treat varargs in several chunks
|
|
// 11/17 - metaprogramming to detect the presence of extension points
|
|
// 11/17 - detect generic lambda
|
|
// 12/17 - investigate SFINAE failure. Reason was indirect use while in template instantiation
|
|
// 03/18 - Dependency Injection / Singleton initialisation / double checked locking
|
|
|
|
|
|
/** @file try.cpp
|
|
** Rework of the template lib::Depend for singleton and service access.
|
|
*/
|
|
|
|
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>
|
|
#include <memory>
|
|
|
|
|
|
#define SHOW_TYPE(_TY_) \
|
|
cout << "typeof( " << STRINGIFY(_TY_) << " )= " << lib::meta::typeStr<_TY_>() <<endl;
|
|
#define SHOW_EXPR(_XX_) \
|
|
cout << "Probe " << STRINGIFY(_XX_) << " ? = " << _XX_ <<endl;
|
|
|
|
|
|
namespace error = lumiera::error;
|
|
|
|
using lib::ClassLock;
|
|
using lib::meta::enable_if;
|
|
|
|
|
|
namespace {
|
|
template<typename TAR, typename SEL =void>
|
|
class InstanceHolder
|
|
: boost::noncopyable
|
|
{
|
|
std::unique_ptr<TAR> instance_;
|
|
|
|
|
|
public:
|
|
TAR*
|
|
buildInstance()
|
|
{
|
|
if (instance_)
|
|
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
|
|
instance_.reset (new TAR{});
|
|
return instance_.get();
|
|
}
|
|
};
|
|
|
|
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.");
|
|
}
|
|
};
|
|
}//(End)Implementation helper
|
|
|
|
|
|
|
|
template<class SRV>
|
|
class DependInject;
|
|
|
|
template<class SRV>
|
|
class Depend
|
|
{
|
|
using Factory = std::function<SRV*()>;
|
|
|
|
static SRV* instance;
|
|
static Factory factory;
|
|
|
|
static InstanceHolder<SRV> singleton;
|
|
|
|
friend class DependInject<SRV>;
|
|
public:
|
|
|
|
SRV&
|
|
operator() ()
|
|
{
|
|
if (!instance)
|
|
retrieveInstance();
|
|
ENSURE (instance);
|
|
return *instance;
|
|
}
|
|
|
|
private:
|
|
void
|
|
retrieveInstance()
|
|
{
|
|
ClassLock<SRV> guard;
|
|
|
|
if (!instance)
|
|
{
|
|
if (!factory)
|
|
{
|
|
instance = singleton.buildInstance();
|
|
factory = disabledFactory;
|
|
}
|
|
else
|
|
instance = factory();
|
|
}
|
|
}
|
|
|
|
static SRV*
|
|
disabledFactory()
|
|
{
|
|
throw error::Fatal("Service not available at this point of the Application Lifecycle"
|
|
,error::LUMIERA_ERROR_LIFECYCLE);
|
|
}
|
|
};
|
|
|
|
|
|
template<class SRV>
|
|
SRV* Depend<SRV>::instance;
|
|
|
|
template<class SRV>
|
|
typename Depend<SRV>::Factory Depend<SRV>::factory;
|
|
|
|
template<class SRV>
|
|
InstanceHolder<SRV> Depend<SRV>::singleton;
|
|
|
|
|
|
///////////////////////////////////////////////////////Configuration
|
|
|
|
using std::move;
|
|
|
|
|
|
template<class SRV>
|
|
class DependInject
|
|
{
|
|
using Factory = typename Depend<SRV>::Factory;
|
|
public:
|
|
static void
|
|
installFactory (Factory&& otherFac)
|
|
{
|
|
ClassLock<SRV> 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);
|
|
}
|
|
|
|
static void
|
|
disableFactory()
|
|
{
|
|
ClassLock<SRV> guard;
|
|
Depend<SRV>::factory = Depend<SRV>::disabledFactory;
|
|
}
|
|
|
|
static void
|
|
activateServiceAccess (SRV& newInstance)
|
|
{
|
|
ClassLock<SRV> guard;
|
|
if (Depend<SRV>::instance)
|
|
throw error::Logic("Attempt to activate an external service implementation, "
|
|
"but another instance has already been dependency-injected."
|
|
, error::LUMIERA_ERROR_LIFECYCLE);
|
|
Depend<SRV>::instance = &newInstance;
|
|
Depend<SRV>::factory = Depend<SRV>::disabledFactory;
|
|
}
|
|
|
|
static void
|
|
deactivateServiceAccess()
|
|
{
|
|
ClassLock<SRV> guard;
|
|
Depend<SRV>::instance = nullptr;
|
|
}
|
|
|
|
|
|
|
|
template<class SUB>
|
|
static void
|
|
useSingleton()
|
|
{
|
|
static InstanceHolder<SUB> singleton;
|
|
installFactory ([&]()
|
|
{
|
|
disableFactory();
|
|
return singleton.buildInstance();
|
|
});
|
|
}
|
|
|
|
|
|
template<class IMP>
|
|
class ServiceInstance
|
|
{
|
|
std::unique_ptr<IMP> instance_;
|
|
|
|
public:
|
|
ServiceInstance()
|
|
: instance_(new IMP{})
|
|
{
|
|
activateServiceAccess (*instance_);
|
|
}
|
|
|
|
~ServiceInstance()
|
|
{
|
|
deactivateServiceAccess();
|
|
}
|
|
|
|
explicit
|
|
operator bool() const
|
|
{
|
|
return bool(instance_);
|
|
}
|
|
|
|
IMP&
|
|
operator* () const
|
|
{
|
|
ENSURE (instance_);
|
|
return *instance_;
|
|
}
|
|
|
|
IMP*
|
|
operator-> () const
|
|
{
|
|
ENSURE (instance_);
|
|
return instance_.get();
|
|
}
|
|
};
|
|
|
|
template<class IMP>
|
|
class Local
|
|
{
|
|
public:
|
|
Local()
|
|
{
|
|
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////Usage
|
|
|
|
struct Dum
|
|
: boost::noncopyable
|
|
{
|
|
virtual ~Dum() { }
|
|
virtual int probe() =0;
|
|
};
|
|
|
|
|
|
int checksum = 0;
|
|
|
|
template<int N>
|
|
struct Dummy
|
|
: Dum
|
|
{
|
|
Dummy() { checksum += N; }
|
|
~Dummy() { checksum -= N; cout << "~Dummy<"<<N<<">"<<endl;}
|
|
|
|
virtual int
|
|
probe() override
|
|
{
|
|
return N * checksum;
|
|
}
|
|
};
|
|
|
|
|
|
using error::LUMIERA_ERROR_LIFECYCLE;
|
|
|
|
int
|
|
main (int, char**)
|
|
{
|
|
Depend<Dummy<1>> dep11;
|
|
Depend<Dummy<5>> dep5;
|
|
Depend<Dummy<1>> dep12;
|
|
|
|
cout << "Siz-DUM : " << lib::test::showSizeof(dep11) << " " << lib::test::showSizeof(dep5) << endl;
|
|
cout << "check-vor="<<checksum<<endl;
|
|
|
|
SHOW_EXPR( dep11().probe() );
|
|
SHOW_EXPR( checksum );
|
|
SHOW_EXPR( dep5().probe() );
|
|
SHOW_EXPR( checksum );
|
|
SHOW_EXPR( dep12().probe() );
|
|
SHOW_EXPR( checksum );
|
|
|
|
Depend<Dum> dumm;
|
|
DependInject<Dum>::useSingleton<Dummy<7>>();
|
|
SHOW_EXPR( dumm().probe() );
|
|
SHOW_EXPR( checksum );
|
|
VERIFY_ERROR (LIFECYCLE, DependInject<Dum>::useSingleton<Dummy<9>>() );
|
|
SHOW_EXPR( Depend<Dum>{}().probe() );
|
|
SHOW_EXPR( checksum );
|
|
|
|
struct SubDummy
|
|
: Dummy<3>
|
|
{
|
|
virtual int
|
|
probe() override
|
|
{
|
|
return -checksum + offset;
|
|
}
|
|
|
|
int offset = 0;
|
|
};
|
|
|
|
Depend<Dummy<3>> dep3;
|
|
SHOW_EXPR( checksum );
|
|
{
|
|
DependInject<Dummy<3>>::ServiceInstance<SubDummy> service{};
|
|
CHECK (service);
|
|
SHOW_EXPR( checksum );
|
|
SHOW_EXPR( dep3().probe() );
|
|
SHOW_EXPR( checksum );
|
|
service->offset = 5;
|
|
SHOW_EXPR( dep3().probe() );
|
|
SHOW_EXPR( checksum );
|
|
}
|
|
SHOW_EXPR( checksum );
|
|
VERIFY_ERROR (LIFECYCLE, dep3().probe() );
|
|
VERIFY_ERROR (LIFECYCLE, DependInject<Dum>::ServiceInstance<SubDummy>{} );
|
|
SHOW_EXPR( checksum );
|
|
|
|
|
|
cout << "\n.gulp.\n";
|
|
|
|
return 0;
|
|
}
|