diff --git a/research/try.cpp b/research/try.cpp index 37862689b..4936767a7 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -65,321 +65,321 @@ typedef unsigned int uint; cout << "Probe " << STRINGIFY(_XX_) << " ? = " << _XX_ < - class InstanceHolder - : boost::noncopyable - { - std::unique_ptr 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(); - } - }; + namespace error = lumiera::error; - template - class InstanceHolder>> - { - 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 DependInject; - -template -class Depend - { - using Factory = std::function; - - static SRV* instance; - static Factory factory; - - static InstanceHolder singleton; - - friend class DependInject; - public: - - SRV& - operator() () + using lib::ClassLock; + using lib::meta::enable_if; + + + namespace { + template + class InstanceHolder + : boost::noncopyable { - if (!instance) - retrieveInstance(); - ENSURE (instance); - return *instance; - } - - private: - void - retrieveInstance() - { - ClassLock guard; + std::unique_ptr instance_; - if (!instance) - { - if (!factory) - instance = singleton.buildInstance(); - else - instance = factory(); - factory = disabledFactory; - } - } - - static SRV* - disabledFactory() - { - throw error::Fatal("Service not available at this point of the Application Lifecycle" - ,error::LUMIERA_ERROR_LIFECYCLE); - } - }; - - -template -SRV* Depend::instance; - -template -typename Depend::Factory Depend::factory; - -template -InstanceHolder Depend::singleton; - - -///////////////////////////////////////////////////////Configuration - -using std::move; - - -template -struct DependInject - { - using Factory = typename Depend::Factory; - - - /** configure dependency-injection for type SRV to build a subclass singleton - * @tparam SUB concrete subclass type to build on demand when invoking `Depend` - * @throws error::Logic (LUMIERA_ERROR_LIFECYCLE) when the default factory has already - * been invoked at the point when calling this (re)configuration function. - */ - template - static void - useSingleton() - { - __assert_compatible(); - static InstanceHolder singleton; - installFactory ([&]() - { - return singleton.buildInstance(); - }); - } - - - /** - * Configuration handle to expose a service implementation through the `Depend` front-end. - * This noncopyable (but movable) handle shall be planted within the context operating the service - * to be exposed. It will immediately create (in RAII style) and manage a heap-allocated instance - * of the subclass `IMP` and expose a baseclass pointer to this specific instance through `Depend`. - * Moreover, the implementation subclass can be accessed through this handle, which acts as smart-ptr. - * When the handle goes out of scope, the implementation instance is destroyed and the access through - * `Depend` is closed and inhibited, to prevent on-demand creation of a baseclass `SRV` singleton. - * @tparam IMP concrete service implementation subclass to build, manage and expose. - * @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 ServiceInstance - { - std::unique_ptr instance_; public: - ServiceInstance() - : instance_(new IMP{}) + TAR* + buildInstance() { - __assert_compatible(); - activateServiceAccess (*instance_); - } - - ~ServiceInstance() - { - deactivateServiceAccess(); - } - - ServiceInstance (ServiceInstance&&) = default; - ServiceInstance (ServiceInstance const&) = delete; - ServiceInstance& operator= (ServiceInstance&&) = delete; - - explicit - operator bool() const - { - return bool(instance_); - } - - IMP& - operator* () const - { - ENSURE (instance_); - return *instance_; - } - - IMP* - operator-> () const - { - ENSURE (instance_); + 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(); } }; - - /** - * Configuration handle for temporarily shadowing a dependency by a test mock instance. - * This noncopyable (but movable) handle shall be planted within the immediate test context. - * It immediately stashes away the existing state and configuration from `Depend`, but - * waits for actual invocation of the `Depend`-front-end to create a heap-allocated - * instance of the `MOC` subclass, which it manages and exposes like a smart-ptr. - * When the handle goes out of scope, the original state and configuration is restored - */ - template - class Local + template + class InstanceHolder>> { - std::unique_ptr mock_; - - SRV* origInstance_; - Factory origFactory_; - public: - Local() + ABS* + buildInstance() { - __assert_compatible(); - temporarilyInstallAlternateFactory (origInstance_, origFactory_ - ,[this]() - { - mock_.reset(new MOC{}); - return mock_.get(); - }); - } - ~Local() - { - restoreOriginalFactory (origInstance_, origFactory_); - } - - Local (Local&&) = default; - Local (Local const&) = delete; - Local& operator= (Local&&) = delete; - - explicit - operator bool() const - { - return bool(mock_); - } - - MOC& - operator* () const - { - ENSURE (mock_); - return *mock_; - } - - MOC* - operator-> () const - { - ENSURE (mock_); - return mock_.get(); + throw error::Fatal("Attempt to create a singleton instance of an abstract class. " + "Application architecture or lifecycle is seriously broken."); } }; - - - - protected: /* ======= internal access-API for those configurations to manipulate Depend ======= */ - template - friend class ServiceInstance; - template - friend class Local; - - - template - static void - __assert_compatible() - { - static_assert (std::is_base_of::value, - "Installed implementation class must be compatible to the interface."); - } - - static void - installFactory (Factory&& otherFac) - { - ClassLock guard; - if (Depend::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::factory = move (otherFac); - } - - static void - temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, Factory&& newFac) - { - ClassLock guard; - stashFac = move(Depend::factory); - stashInstance = Depend::instance; - Depend::factory = move(newFac); - Depend::instance = nullptr; - } - - static void - restoreOriginalFactory (SRV*& stashInstance, Factory& stashFac) - { - ClassLock guard; - Depend::factory = move(stashFac); - Depend::instance = stashInstance; - } - - static void - activateServiceAccess (SRV& newInstance) - { - ClassLock guard; - if (Depend::instance) - throw error::Logic("Attempt to activate an external service implementation, " - "but another instance has already been dependency-injected." - , error::LUMIERA_ERROR_LIFECYCLE); - Depend::instance = &newInstance; - Depend::factory = Depend::disabledFactory; - } - - static void - deactivateServiceAccess() - { - ClassLock guard; - Depend::instance = nullptr; - Depend::factory = Depend::disabledFactory; - } - }; - - + }//(End)Implementation helper + + + + template + class DependInject; + + template + class Depend + { + using Factory = std::function; + + static SRV* instance; + static Factory factory; + + static InstanceHolder singleton; + + friend class DependInject; + public: + + SRV& + operator() () + { + if (!instance) + retrieveInstance(); + ENSURE (instance); + return *instance; + } + + private: + void + retrieveInstance() + { + ClassLock guard; + + if (!instance) + { + if (!factory) + instance = singleton.buildInstance(); + else + instance = factory(); + factory = disabledFactory; + } + } + + static SRV* + disabledFactory() + { + throw error::Fatal("Service not available at this point of the Application Lifecycle" + ,error::LUMIERA_ERROR_LIFECYCLE); + } + }; + + + template + SRV* Depend::instance; + + template + typename Depend::Factory Depend::factory; + + template + InstanceHolder Depend::singleton; + + + ///////////////////////////////////////////////////////Configuration + + using std::move; + + + template + struct DependInject + { + using Factory = typename Depend::Factory; + + + /** configure dependency-injection for type SRV to build a subclass singleton + * @tparam SUB concrete subclass type to build on demand when invoking `Depend` + * @throws error::Logic (LUMIERA_ERROR_LIFECYCLE) when the default factory has already + * been invoked at the point when calling this (re)configuration function. + */ + template + static void + useSingleton() + { + __assert_compatible(); + static InstanceHolder singleton; + installFactory ([&]() + { + return singleton.buildInstance(); + }); + } + + + /** + * Configuration handle to expose a service implementation through the `Depend` front-end. + * This noncopyable (but movable) handle shall be planted within the context operating the service + * to be exposed. It will immediately create (in RAII style) and manage a heap-allocated instance + * of the subclass `IMP` and expose a baseclass pointer to this specific instance through `Depend`. + * Moreover, the implementation subclass can be accessed through this handle, which acts as smart-ptr. + * When the handle goes out of scope, the implementation instance is destroyed and the access through + * `Depend` is closed and inhibited, to prevent on-demand creation of a baseclass `SRV` singleton. + * @tparam IMP concrete service implementation subclass to build, manage and expose. + * @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 ServiceInstance + { + std::unique_ptr instance_; + + public: + ServiceInstance() + : instance_(new IMP{}) + { + __assert_compatible(); + activateServiceAccess (*instance_); + } + + ~ServiceInstance() + { + deactivateServiceAccess(); + } + + ServiceInstance (ServiceInstance&&) = default; + ServiceInstance (ServiceInstance const&) = delete; + ServiceInstance& operator= (ServiceInstance&&) = delete; + + explicit + operator bool() const + { + return bool(instance_); + } + + IMP& + operator* () const + { + ENSURE (instance_); + return *instance_; + } + + IMP* + operator-> () const + { + ENSURE (instance_); + return instance_.get(); + } + }; + + + /** + * Configuration handle for temporarily shadowing a dependency by a test mock instance. + * This noncopyable (but movable) handle shall be planted within the immediate test context. + * It immediately stashes away the existing state and configuration from `Depend`, but + * waits for actual invocation of the `Depend`-front-end to create a heap-allocated + * instance of the `MOC` subclass, which it manages and exposes like a smart-ptr. + * When the handle goes out of scope, the original state and configuration is restored + */ + template + class Local + { + std::unique_ptr mock_; + + SRV* origInstance_; + Factory origFactory_; + + public: + Local() + { + __assert_compatible(); + temporarilyInstallAlternateFactory (origInstance_, origFactory_ + ,[this]() + { + mock_.reset(new MOC{}); + return mock_.get(); + }); + } + ~Local() + { + restoreOriginalFactory (origInstance_, origFactory_); + } + + Local (Local&&) = default; + Local (Local const&) = delete; + Local& operator= (Local&&) = delete; + + explicit + operator bool() const + { + return bool(mock_); + } + + MOC& + operator* () const + { + ENSURE (mock_); + return *mock_; + } + + MOC* + operator-> () const + { + ENSURE (mock_); + return mock_.get(); + } + }; + + + + protected: /* ======= internal access-API for those configurations to manipulate Depend ======= */ + template + friend class ServiceInstance; + template + friend class Local; + + + template + static void + __assert_compatible() + { + static_assert (std::is_base_of::value, + "Installed implementation class must be compatible to the interface."); + } + + static void + installFactory (Factory&& otherFac) + { + ClassLock guard; + if (Depend::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::factory = move (otherFac); + } + + static void + temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, Factory&& newFac) + { + ClassLock guard; + stashFac = move(Depend::factory); + stashInstance = Depend::instance; + Depend::factory = move(newFac); + Depend::instance = nullptr; + } + + static void + restoreOriginalFactory (SRV*& stashInstance, Factory& stashFac) + { + ClassLock guard; + Depend::factory = move(stashFac); + Depend::instance = stashInstance; + } + + static void + activateServiceAccess (SRV& newInstance) + { + ClassLock guard; + if (Depend::instance) + throw error::Logic("Attempt to activate an external service implementation, " + "but another instance has already been dependency-injected." + , error::LUMIERA_ERROR_LIFECYCLE); + Depend::instance = &newInstance; + Depend::factory = Depend::disabledFactory; + } + + static void + deactivateServiceAccess() + { + ClassLock guard; + Depend::instance = nullptr; + Depend::factory = Depend::disabledFactory; + } + }; + + ///////////////////////////////////////////////////////Usage struct Dum @@ -413,124 +413,124 @@ using error::LUMIERA_ERROR_FATAL; int main (int, char**) { - Depend> dep11; - Depend> dep5; - Depend> dep12; - - cout << "Siz-DUM : " << lib::test::showSizeof(dep11) << " " << lib::test::showSizeof(dep5) << endl; - cout << "check-vor="<{}() ); - - Depend dumm; - DependInject::useSingleton>(); - SHOW_EXPR( dumm().probe() ); - SHOW_EXPR( checksum ); - VERIFY_ERROR (LIFECYCLE, DependInject::useSingleton>() ); - SHOW_EXPR( Depend{}().probe() ); - SHOW_EXPR( checksum ); - - struct SubDummy - : Dummy<3> - { - virtual int - probe() override - { - return -checksum + offset; - } - - int offset = 0; - }; - - Depend> dep3; - SHOW_EXPR( checksum ); - { - DependInject>::ServiceInstance 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::ServiceInstance{} ); - SHOW_EXPR( checksum ); - - { - DependInject::Local mockDum; - DependInject>::Local mockDummy3; - CHECK (!mockDum); - CHECK (!mockDummy3); - SHOW_EXPR( dumm().probe() ); - CHECK ( mockDum); - CHECK (!mockDummy3); - SHOW_EXPR( checksum ); - SHOW_EXPR( mockDum->probe() ); - SHOW_EXPR( checksum ); - mockDum->offset = -4; - SHOW_EXPR( dumm().probe() ); + Depend> dep11; + Depend> dep5; + Depend> dep12; + + cout << "Siz-DUM : " << lib::test::showSizeof(dep11) << " " << lib::test::showSizeof(dep5) << endl; + cout << "check-vor="<{}() ); - CHECK (!mockDummy3); - SHOW_EXPR( checksum ); - SHOW_EXPR( dep3().probe() ); - SHOW_EXPR( checksum ); - CHECK ( mockDummy3); - SHOW_EXPR( mockDummy3->probe() ); - SHOW_EXPR( checksum ); - mockDummy3->offset = 19; - SHOW_EXPR( dep3().probe() ); - mockDum->offset = -6; - SHOW_EXPR( dep3().probe() ); - SHOW_EXPR( dumm().probe() ); - SHOW_EXPR( checksum ); - } - SHOW_EXPR( checksum ); - SHOW_EXPR( dumm().probe() ); - VERIFY_ERROR (LIFECYCLE, dep3().probe() ); - SHOW_EXPR( checksum ); - { - DependInject>::ServiceInstance service{}; - SHOW_EXPR( checksum ); - SHOW_EXPR( dep3().probe() ); - service->offset = 5; - SHOW_EXPR( dep3().probe() ); - SHOW_EXPR( checksum ); - { - DependInject>::Local mockDummy31; - CHECK (!mockDummy31); - SHOW_EXPR( checksum ); - SHOW_EXPR( dep3().probe() ); - SHOW_EXPR( checksum ); - mockDummy31->offset = 10; - SHOW_EXPR( dep3().probe() ); - SHOW_EXPR( mockDummy31->probe() ); - SHOW_EXPR( service->probe() ); - CHECK (mockDummy31->offset != service->offset); - service->offset = 20; - SHOW_EXPR( dep3().probe() ); - SHOW_EXPR( mockDummy31->probe() ); - SHOW_EXPR( service->probe() ); - SHOW_EXPR( checksum ); - } - SHOW_EXPR( checksum ); - SHOW_EXPR( dep3().probe() ); - SHOW_EXPR( checksum ); - } - SHOW_EXPR( checksum ); - VERIFY_ERROR (LIFECYCLE, dep3().probe() ); - SHOW_EXPR( dumm().probe() ); - SHOW_EXPR( checksum ); + Depend dumm; + DependInject::useSingleton>(); + SHOW_EXPR( dumm().probe() ); + SHOW_EXPR( checksum ); + VERIFY_ERROR (LIFECYCLE, DependInject::useSingleton>() ); + SHOW_EXPR( Depend{}().probe() ); + SHOW_EXPR( checksum ); + + struct SubDummy + : Dummy<3> + { + virtual int + probe() override + { + return -checksum + offset; + } + + int offset = 0; + }; + + Depend> dep3; + SHOW_EXPR( checksum ); + { + DependInject>::ServiceInstance 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::ServiceInstance{} ); + SHOW_EXPR( checksum ); + + { + DependInject::Local mockDum; + DependInject>::Local mockDummy3; + CHECK (!mockDum); + CHECK (!mockDummy3); + SHOW_EXPR( dumm().probe() ); + CHECK ( mockDum); + CHECK (!mockDummy3); + SHOW_EXPR( checksum ); + SHOW_EXPR( mockDum->probe() ); + SHOW_EXPR( checksum ); + mockDum->offset = -4; + SHOW_EXPR( dumm().probe() ); + + CHECK (!mockDummy3); + SHOW_EXPR( checksum ); + SHOW_EXPR( dep3().probe() ); + SHOW_EXPR( checksum ); + CHECK ( mockDummy3); + SHOW_EXPR( mockDummy3->probe() ); + SHOW_EXPR( checksum ); + mockDummy3->offset = 19; + SHOW_EXPR( dep3().probe() ); + mockDum->offset = -6; + SHOW_EXPR( dep3().probe() ); + SHOW_EXPR( dumm().probe() ); + SHOW_EXPR( checksum ); + } + SHOW_EXPR( checksum ); + SHOW_EXPR( dumm().probe() ); + VERIFY_ERROR (LIFECYCLE, dep3().probe() ); + SHOW_EXPR( checksum ); + { + DependInject>::ServiceInstance service{}; + SHOW_EXPR( checksum ); + SHOW_EXPR( dep3().probe() ); + service->offset = 5; + SHOW_EXPR( dep3().probe() ); + SHOW_EXPR( checksum ); + { + DependInject>::Local mockDummy31; + CHECK (!mockDummy31); + SHOW_EXPR( checksum ); + SHOW_EXPR( dep3().probe() ); + SHOW_EXPR( checksum ); + mockDummy31->offset = 10; + SHOW_EXPR( dep3().probe() ); + SHOW_EXPR( mockDummy31->probe() ); + SHOW_EXPR( service->probe() ); + CHECK (mockDummy31->offset != service->offset); + service->offset = 20; + SHOW_EXPR( dep3().probe() ); + SHOW_EXPR( mockDummy31->probe() ); + SHOW_EXPR( service->probe() ); + SHOW_EXPR( checksum ); + } + SHOW_EXPR( checksum ); + SHOW_EXPR( dep3().probe() ); + SHOW_EXPR( checksum ); + } + SHOW_EXPR( checksum ); + VERIFY_ERROR (LIFECYCLE, dep3().probe() ); + SHOW_EXPR( dumm().probe() ); + SHOW_EXPR( checksum ); cout << "\n.gulp.\n";