Base
and two immediate heirs from it: Derived1
and Derived2
, the instances of which we want to create by the factory. #include <iostream> #include <string> #include "factory.h" using namespace std; class Animal{ public: Animal(bool isAlive,string name) : isAlive(isAlive),name(name){}; bool isAlive; string name; virtual string voice() const=0; }; class Dog : public Animal{ public: using Animal::Animal; string voice() const{ return this->isAlive? "Woof! I'm "+this->name+"\n": ""; } }; class Cat : public Animal{ public: using Animal::Animal; string voice() const{ return this->isAlive? "Meow, I'm "+this->name+"\n": ""; } }; int main(void){ GenericObjectFactory<string,Animal,bool,string> animalFactory; animalFactory.add<Dog>("man's friend"); animalFactory.add<Cat>("^_^"); Animal *dog1=animalFactory.get("man's friend")(true,"charlie"); Animal *dog2=animalFactory.get("man's friend")(false,"fido"); Animal *cat =animalFactory.get("^_^")(true,"begemoth"); cout << dog1->voice() << dog2->voice() << cat ->voice(); return 0; }
Thus, the animalFactory
factory is first created from the animalFactory
template class, the first required parameter of which is the key type (in this example, a string, but it can also be handy to use something else, such as an integer value or an enumeration) of a map
type, where the values are the classes that will then need to be added to the factory so that it can create them; as the second mandatory parameter - the base class of these classes (and they must be inherited from one class); the remaining two parameters, which can be any number (including zero), are the types of the arguments of the class constructor created by the factory, in turn.add
, specifying, as the first and only parameter, the class that the factory needs to register, and the first and only argument is the identifier of this class, in our case the string (defined the first parameter of the template class).get
takes the id string as the first and only argument, and returns the so-called instantiator of the class found by this id. Instantiator is such a function that will take a set of arguments (in the number and types specified in the factory specialization) and create an instance of the desired class, passing them to the constructor. A kind of proxy, which is needed later I will explain why. It is very similar to the fact that the constructor itself was returned. :) It will return an instantiator with a pointer to the created object, but this pointer is of a type not of the inherited, but of the base class. With a great desire, then you can dynamic_cast
'thread. auto catInstantiator=animalFactory.get("^_^");
, and already to create objects later, when it will be necessary: Animal *cat=catInstantiator(true,"delayed cat"); Animal *cat=catInstantiator(false,"dead delayed cat");
:)And that's it! Isn't it elengant? It seems to me that I exceeded the rtorsten solution proposed in his article at least three times.
- #include <map>
- template < class ID, class Base, class ... Args > class GenericObjectFactory {
- private :
- typedef Base * ( * fInstantiator ) ( Args ... ) ;
- template < class Derived > static Base * instantiator ( Args ... args ) {
- return new Derived ( args ... ) ;
- }
- std :: map < ID, fInstantiator > classes ;
- public :
- GenericObjectFactory ( ) { }
- template < class Derived > void add ( ID id ) {
- classes [ id ] = & instantiator < Derived > ;
- }
- fInstantiator get ( id id ) {
- return classes [ id ] ;
- }
- } ;
map
container. In this case, take STL-evsky. But the QMap
also fits without problems.fInstantiator
type. It is a pointer to a function that returns a pointer to an object of type Base
(a template parameter) and accepts arguments of types Args ...
(also a template parameter, but there can be an arbitrary number, see variadic templates ) returned by the member function get
(line 16 ).instantiator
, about which we spoke, with the template. This is necessary so that this function can be specialized with the Derived
class added to the factory , which makes the template member function void add<class Derived>
, and then take it to the specialized function address (which only creates objects of this class) and place to the container std::map classes
as a value, where the key is an argument of the void add<class Derived>
function void add<class Derived>
type of ID
(the first parameter of the factory template). instantiator
declared static
for simplicity, but if you really need, you can use the so-called member function pointers .instantiator
, in fact, accepts their arguments. Of course, in the same number and types, and of course, this is all known at compile time, and if none of the constructors of any of the classes added to the factory accepts arguments of these types, the compiler throws an error.Base
), the function has the same signature, that is, always (with any parameter of the template) takes the known and the same number and type of arguments, and always returns the value of the same known type - a pointer to an object of type Base
. That is, the original problem is solved! :)add
function adds to std::map classes
pair of id
types ID
(the first parameter of the class template) and the address of the static instantiator
member function that was just specialized. Already spoken.get
function returns a pointer to the instantiator
desired class. Please note that at the moment an object is not created, the user then does it himself using the issued instantiator
. This is exactly what I wanted to achieve.#include "factory.h"
and check the result in your favorite compiler, without forgetting to include support for the C ++ 11 standard.Source: https://habr.com/ru/post/229993/
All Articles