The factory_mgr class is essentially a static classloader, capable of loading classes by using registered factories for a given set of keys (e.g., class names). More...
#include <factory.hpp>
Public Types | |
typedef KeyType | key_type |
A typedef for the KeyType used by this class. | |
typedef InterfaceT | value_type |
A typedef for the InterfaceT used by this class. | |
typedef InterfaceT * | result_type |
Same as (InterfaceT *). | |
typedef aliaser< key_type > | aliaser_type |
A type used to map classname aliases for this factory manager. | |
typedef result_type(* | factory_function_type )() |
The type of factories used by this class: a function taking void and returning (value_type ). | |
typedef std::map< key_type, factory_function_type > | factory_map_type |
Internal container type used for mapping keys to factories. | |
Public Member Functions | |
factory_mgr () | |
virtual | ~factory_mgr () throw () |
aliaser_type & | aliases () |
Returns the container of classname aliases. | |
const aliaser_type & | aliases () const |
Const overload. | |
virtual result_type | create (const key_type &_key) |
Tries to instantiate an instance of value_type using the given key. | |
result_type | operator() (const key_type &key) |
Returns create(key). | |
virtual void | destroy (result_type obj) |
Simply calls delete obj. | |
virtual void | register_factory (const key_type &key, factory_function_type fp) |
Registers a factory using the given key. | |
factory_map_type & | factory_map () |
Returns the internal key-to-factory map. | |
const factory_map_type & | factory_map () const |
Const overload. | |
virtual bool | provides (const key_type &key) const |
Returns true if the given key is registered (alias expansions are considered). | |
Static Public Member Functions | |
static factory_mgr & | instance () |
Returns a shared reference to a factory. |
The factory_mgr class is essentially a static classloader, capable of loading classes by using registered factories for a given set of keys (e.g., class names).
Classloaders, at least in my experience, need to be able to load all classes which derive from some given type. Without a common base class, one can't safely attempt to cast from an arbitrary pointer to the type we want to load. That's where the InterfaceT parameter comes in. All objects instantiated via this loader must inherit (be-a) from InterfaceT, or must literally be InterfaceT.
KeyType is a type which specifies the type of key used to look up classes, defaulting to std::string.
For this implementation, both InterfaceT and KeyType must be Default Constructable, and InterfaceT must be constructable on the heap (e.g., via new InterfaceT()).
InterfaceT must be a "plain type", without any pointer, reference or const qualifications.
Sample usage:
factory_mgr<MyInterface> & fac = factory_mgr<MyInterface>::instance(); fac.register_factory( "my_key", s11n::fac::create_hook<MyInterface,MyClass>::create ); MyInterface *foo = fac.create( "some_key" ); // == NULL foo = fac.create( "my_key" ); // == a new MyClass object
Note that all instances of factory_mgr share the same factories and aliases maps. This is a design decision which is intended to simplify usage of the type and ensure consistency of state across module boundaries. In effect, the default implementation is a Monostate type, which each instance sharing the same data.
Trivia: above i mentioned "casting", but this implementation requires no casting of any type, neither in the library nor in client code (unless the client explicitely needs to do so for their own purposes).
Definition at line 253 of file factory.hpp.
typedef aliaser<key_type> s11n::fac::factory_mgr< InterfaceT, KeyType >::aliaser_type |
A type used to map classname aliases for this factory manager.
Definition at line 287 of file factory.hpp.
typedef result_type( * s11n::fac::factory_mgr< InterfaceT, KeyType >::factory_function_type)() |
The type of factories used by this class: a function taking void and returning (value_type ).
See factory_map().
todo: implement proper functor support.
Definition at line 295 of file factory.hpp.
typedef std::map< key_type, factory_function_type > s11n::fac::factory_mgr< InterfaceT, KeyType >::factory_map_type |
Internal container type used for mapping keys to factories.
Definition at line 301 of file factory.hpp.
typedef KeyType s11n::fac::factory_mgr< InterfaceT, KeyType >::key_type |
A typedef for the KeyType used by this class.
Definition at line 265 of file factory.hpp.
typedef InterfaceT* s11n::fac::factory_mgr< InterfaceT, KeyType >::result_type |
Same as (InterfaceT *).
Definition at line 276 of file factory.hpp.
typedef InterfaceT s11n::fac::factory_mgr< InterfaceT, KeyType >::value_type |
A typedef for the InterfaceT used by this class.
For conformance with the Adaptable Unary Functor model
Definition at line 272 of file factory.hpp.
s11n::fac::factory_mgr< InterfaceT, KeyType >::factory_mgr | ( | ) | [inline] |
Definition at line 279 of file factory.hpp.
virtual s11n::fac::factory_mgr< InterfaceT, KeyType >::~factory_mgr | ( | ) | throw () [inline, virtual] |
Definition at line 281 of file factory.hpp.
const aliaser_type& s11n::fac::factory_mgr< InterfaceT, KeyType >::aliases | ( | ) | const [inline] |
aliaser_type& s11n::fac::factory_mgr< InterfaceT, KeyType >::aliases | ( | ) | [inline] |
Returns the container of classname aliases.
In this implementation the aliases are shared amongst all instances of this type, because that aligns it with the shared factory map.
Definition at line 311 of file factory.hpp.
References s11nlite::instance().
virtual result_type s11n::fac::factory_mgr< InterfaceT, KeyType >::create | ( | const key_type & | _key | ) | [inline, virtual] |
Tries to instantiate an instance of value_type using the given key.
Returns 0 if no object could be loaded for the given key.
Subtypes are free to implement, e.g., DLL lookups.
This implementation calls aliases().expand(_key) to expand any aliased class names. Subclasses/specializations "should" do the same, but are not strictly required to.
Definition at line 337 of file factory.hpp.
virtual void s11n::fac::factory_mgr< InterfaceT, KeyType >::destroy | ( | result_type | obj | ) | [inline, virtual] |
Simply calls delete obj.
Subclasses are free to do custom accounting, garbage collection, or whatever, by overriding this.
Note that it is NOT practical to expect all clients to call this in order to destroy their objects, so the utility of this function is HIGHLY arguable.
Also be aware that the simple delete behaviour is not suitable if InterfaceT is a populated container of dumb pointers, or otherwise contains unowned pointers.
Subclasses are free to "suggest" a must-destroy() policy if they wish, but cannot expect anyone to actually follow that wish. In pedantic terms, however, one should destroy an object from the same code module and thread which created it, to avoid low-level problems involving crossing module/thread boundaries. That's what Sutter and Alexandrescu say, anyway, and i'm not one to argue with them.
Definition at line 383 of file factory.hpp.
const factory_map_type& s11n::fac::factory_mgr< InterfaceT, KeyType >::factory_map | ( | ) | const [inline] |
factory_map_type& s11n::fac::factory_mgr< InterfaceT, KeyType >::factory_map | ( | ) | [inline] |
Returns the internal key-to-factory map.
It is safe for clients to modify this except in multi-threaded environments, and then all guarantees go out the window. That said, it should never be necessary for clients to use this.
The default implementation returns the same map for all instances of ThisType. The reasoning behind using static factory maps is essentially this: we can only have one definition of each type. We normally want factories to always return an instance constructed in the same way. If we allow multiple factories per type we might introduce hard-to-track inconsistencies in client code, where different factories than intended are accidentally used. OTOH, private factory maps would open up some interesting possibilities.
TODO (well, to consider): use a hook or Traits type to allow the user to replace this object with his own, so that he can control the scope of the map more fully. Overkill. YAGNI. Christian Prochnow's experience in the P::Classes tree suggests that that would be Bad, anyway, because clients linking from different sources can end up with different instances of the map. This has never been witnessed using phoenix<> to share instances, however.
Definition at line 457 of file factory.hpp.
References s11nlite::instance().
static factory_mgr& s11n::fac::factory_mgr< InterfaceT, KeyType >::instance | ( | ) | [inline, static] |
Returns a shared reference to a factory.
The s11n core always calls this function to get factory instances.
Definition at line 497 of file factory.hpp.
References s11nlite::instance().
result_type s11n::fac::factory_mgr< InterfaceT, KeyType >::operator() | ( | const key_type & | key | ) | [inline] |
virtual bool s11n::fac::factory_mgr< InterfaceT, KeyType >::provides | ( | const key_type & | key | ) | const [inline, virtual] |
Returns true if the given key is registered (alias expansions are considered).
This is sometimes useful for checking whether a factory needs to be re-registered, which is sometimes necessary post-main(), when the internal map gets hosed before clients are done using it.
The constness is arguable: subclasses/specializations could arguably do pre-lookups for DLLs (or similar) here, and might need non-const behaviour. As a consolation, we have made create() non-const, so (provides()==false) can symantically mean "we need to do a DLL lookup".
Definition at line 486 of file factory.hpp.
virtual void s11n::fac::factory_mgr< InterfaceT, KeyType >::register_factory | ( | const key_type & | key, | |
factory_function_type | fp | |||
) | [inline, virtual] |
Registers a factory using the given key.
Note that fp may not be declared as returning a type other than (value_type *), but the actual object it creates may be a polymorphic subclass of value_type. See the create_hook class for a factory which does this subtype-to-base conversion.
For ABSTRACT interface types, you "should" register a no-op factory which simply returns 0. The difference between registering a no-op factory and registering NO factory is that when create() is called, if NO factory is found, this type is free to implement extra logic like DLL lookups to try to find the type (which is futile for an abstract type). However, if it has a registered factory, it will not do this (it doesn't care if the factory returns 0 or not).
Subclasses are free to add behaviour, like instrumentation, but should not forget to actually carry through on the registration part (e.g., by calling this implementation).
Definition at line 417 of file factory.hpp.