215 lines
7.5 KiB
C++
215 lines
7.5 KiB
C++
/*
|
|
DEPENDENCY-FACTORY.hpp - managing the lifecycle of singletons and dependencies
|
|
|
|
Copyright (C) Lumiera.org
|
|
2013, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef LIB_DEPENDENCY_FACTORY_H
|
|
#define LIB_DEPENDENCY_FACTORY_H
|
|
|
|
|
|
|
|
#include "lib/nobug-init.hpp"
|
|
#include "lib/error.hpp"
|
|
|
|
|
|
namespace lib {
|
|
|
|
namespace error = lumiera::error;
|
|
|
|
|
|
/**
|
|
* @internal Factory to generate and manage service objects classified by type.
|
|
* An instance of this factory is placed <i>once for each type</i> for use by
|
|
* the \c lib::Depend<TY> front-end for dependency management. While the latter
|
|
* provides the singleton-style initialisation patter, the DependencyFacotry
|
|
* maintains a customisable factory function for instance creation. Moreover,
|
|
* the embedded helper template DependencyFactory::InstanceHolder actually
|
|
* creates and manages the singleton instances in default configuration;
|
|
* it is placed into a function-scope static variable; consequently
|
|
* the singleton instances are placed into static memory by default.
|
|
*/
|
|
class DependencyFactory
|
|
{
|
|
public:
|
|
typedef void* (*InstanceConstructor)(void);
|
|
typedef void (*KillFun) (void*);
|
|
|
|
/** ensure initialisation by installing a default constructor function,
|
|
* but don't change an explicitly installed different constructor function.
|
|
* @remark deliberately this DependencyFactory has no constructor to
|
|
* initialise the object field \c ctorFunction_ to zero.
|
|
* The reason is, in the intended usage scenario, the
|
|
* DependencyFactory lives within a static variable,
|
|
* which might be constructed in no defined order
|
|
* in relation to the Depend<TY> instance.
|
|
*/
|
|
void
|
|
ensureInitialisation (InstanceConstructor defaultCtor)
|
|
{
|
|
if (!ctorFunction_)
|
|
this->ctorFunction_ = defaultCtor;
|
|
ENSURE (ctorFunction_);
|
|
}
|
|
|
|
|
|
/** explicitly set up constructor function, unless already configured
|
|
* In the default configuration, the template \c Depend<TY> installs a
|
|
* builder function to create a singleton instance in static memory.
|
|
* But specific instances might install e.g. a factory to create a
|
|
* implementation defined subclass; this might also be the place
|
|
* to hook in some kind of centralised service manager in future.
|
|
* @param ctor a function to be invoked to create a new service instance
|
|
* @throw error::Fatal when attempting to change an existing configuration.
|
|
*/
|
|
void
|
|
installConstructorFunction (InstanceConstructor ctor)
|
|
{
|
|
if (ctorFunction_ && ctor != ctorFunction_)
|
|
throw error::Fatal ("DependencyFactory: attempt to change the instance builder function "
|
|
"after-the-fact. Before this call, a different function was installed "
|
|
"and possibly also used already. Hint: visit all code locations, which "
|
|
"actually create an instance of the Depend<TY> template."
|
|
,error::LUMIERA_ERROR_LIFECYCLE);
|
|
this->ctorFunction_ = ctor;
|
|
}
|
|
|
|
|
|
|
|
/** invoke the installed ctor function */
|
|
void*
|
|
buildInstance()
|
|
{
|
|
if (!ctorFunction_)
|
|
throw error::Fatal ("lib::Depend: attempt to retrieve a service object prior to initialisation "
|
|
"of the DependencyFactory. Typically, this happens due to a misconfiguration "
|
|
"regarding static initialisation order. When lib::Depend<TY> is placed into "
|
|
"a class static variable, then the definition and initialisation of that "
|
|
"variable must happen prior to the call which caused this exception."
|
|
,error::LUMIERA_ERROR_LIFECYCLE);
|
|
return ctorFunction_();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
/** pointer to the concrete function
|
|
* used for building new service instances */
|
|
InstanceConstructor ctorFunction_;
|
|
|
|
|
|
|
|
/** function to build service instances.
|
|
* A service class with private ctor can declare DependencyFactory as friend,
|
|
* to indicate this is the expected way to create instances */
|
|
template<typename TAR>
|
|
static TAR*
|
|
create_in_buffer (void* buffer)
|
|
{
|
|
return new(buffer) TAR;
|
|
}
|
|
|
|
|
|
/**
|
|
* @internal Helper to manage a service instance within an embedded buffer.
|
|
* This helper and thus the service instance will be allocated into static memory.
|
|
*/
|
|
template<typename TAR>
|
|
class InstanceHolder
|
|
: boost::noncopyable
|
|
{
|
|
/** storage for the service instance */
|
|
char buff_[sizeof(TAR)];
|
|
uint lifecycle_;
|
|
|
|
|
|
public:
|
|
InstanceHolder()
|
|
: lifecycle_(0)
|
|
{ }
|
|
|
|
~InstanceHolder()
|
|
{
|
|
lifecycle_ |= 4;
|
|
if (1 & lifecycle_)
|
|
{
|
|
reinterpret_cast<TAR&> (buff_). ~TAR();
|
|
--lifecycle_;
|
|
} }
|
|
|
|
|
|
TAR*
|
|
buildInstance ()
|
|
{
|
|
if (0 < lifecycle_)
|
|
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 = create_in_buffer<TAR>(buff_);
|
|
++lifecycle_;
|
|
return newInstance;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
template<class TAR>
|
|
static void*
|
|
createSingletonInstance()
|
|
{
|
|
static InstanceHolder<TAR> storage; // note: the singleton(s) live here
|
|
return storage.buildInstance();
|
|
}
|
|
|
|
|
|
template<class TAR>
|
|
friend InstanceConstructor buildSingleton();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* DSL-style marker function for client code
|
|
* to configure the usage of a specific subclass.
|
|
* Typically this function is used right within the
|
|
* Constructor call for lib::Depend; this allows to
|
|
* confine the actual service implementation class
|
|
* to a single compilation unit, without the need
|
|
* for clients of the respective service to know
|
|
* the actual concrete implementation class
|
|
*/
|
|
template<class TAR>
|
|
inline DependencyFactory::InstanceConstructor
|
|
buildSingleton()
|
|
{
|
|
return & DependencyFactory::createSingletonInstance<TAR>;
|
|
}
|
|
|
|
|
|
} // namespace lib
|
|
#endif
|