160

I have a file: Base.h

class Base;
class DerivedA : public Base;
class DerivedB : public Base;

/*etc...*/

and another file: BaseFactory.h

#include "Base.h"

class BaseFactory
{
public:
  BaseFactory(const string &sClassName){msClassName = sClassName;};

  Base * Create()
  {
    if(msClassName == "DerivedA")
    {
      return new DerivedA();
    }
    else if(msClassName == "DerivedB")
    {
      return new DerivedB();
    }
    else if(/*etc...*/)
    {
      /*etc...*/
    }
  };
private:
  string msClassName;
};

/*etc.*/

Is there a way to somehow convert this string to an actual type (class), so that BaseFactory wouldn't have to know all the possible Derived classes, and have if() for each one of them? Can I produce a class from this string?

I think this can be done in C# through Reflection. Is there something similar in C++?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Gal Goldman
  • 8,641
  • 11
  • 45
  • 45

12 Answers12

246

Nope, there is none, unless you do the mapping yourself. C++ has no mechanism to create objects whose types are determined at runtime. You can use a map to do that mapping yourself, though:

template<typename T> Base * createInstance() { return new T; }

typedef std::map<std::string, Base*(*)()> map_type;

map_type map;
map["DerivedA"] = &createInstance<DerivedA>;
map["DerivedB"] = &createInstance<DerivedB>;

And then you can do

return map[some_string]();

Getting a new instance. Another idea is to have the types register themself:

// in base.hpp:
template<typename T> Base * createT() { return new T; }

struct BaseFactory {
    typedef std::map<std::string, Base*(*)()> map_type;

    static Base * createInstance(std::string const& s) {
        map_type::iterator it = getMap()->find(s);
        if(it == getMap()->end())
            return 0;
        return it->second();
    }

protected:
    static map_type * getMap() {
        // never delete'ed. (exist until program termination)
        // because we can't guarantee correct destruction order 
        if(!map) { map = new map_type; } 
        return map; 
    }

private:
    static map_type * map;
};

template<typename T>
struct DerivedRegister : BaseFactory { 
    DerivedRegister(std::string const& s) { 
        getMap()->insert(std::make_pair(s, &createT<T>));
    }
};

// in derivedb.hpp
class DerivedB {
    ...;
private:
    static DerivedRegister<DerivedB> reg;
};

// in derivedb.cpp:
DerivedRegister<DerivedB> DerivedB::reg("DerivedB");

You could decide to create a macro for the registration

#define REGISTER_DEC_TYPE(NAME) \
    static DerivedRegister<NAME> reg

#define REGISTER_DEF_TYPE(NAME) \
    DerivedRegister<NAME> NAME::reg(#NAME)

I'm sure there are better names for those two though. Another thing which probably makes sense to use here is shared_ptr.

If you have a set of unrelated types that have no common base-class, you can give the function pointer a return type of boost::variant<A, B, C, D, ...> instead. Like if you have a class Foo, Bar and Baz, it looks like this:

typedef boost::variant<Foo, Bar, Baz> variant_type;
template<typename T> variant_type createInstance() { 
    return variant_type(T()); 
}

typedef std::map<std::string, variant_type (*)()> map_type;

A boost::variant is like an union. It knows which type is stored in it by looking what object was used for initializing or assigning to it. Have a look at its documentation here. Finally, the use of a raw function pointer is also a bit oldish. Modern C++ code should be decoupled from specific functions / types. You may want to look into Boost.Function to look for a better way. It would look like this then (the map):

typedef std::map<std::string, boost::function<variant_type()> > map_type;

std::function will be available in the next version of C++ too, including std::shared_ptr.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 4
    Loved the idea that the derived classes will register themselves. It's exactly what I was looking for, a way to remove the hard-coded knowledge of which derived classes exist from the factory. – Gal Goldman Feb 25 '09 at 07:50
  • 1
    Originally posted by somedave in another question, this code fails on VS2010 with ambiguous template errors because of make_pair. To fix, change make_pair to std::pair and it should fix those errors. I also got some linking errors which were fixed by adding BaseFactory::map_type* BaseFactory::map = new map_type(); to base.cpp – Spencer Rose Sep 25 '11 at 01:21
  • I am having problems implementing this, using Vs2010 and using std::pair instead of make_pair. When compiling i get link errors. Adding my counterpart to static DerivedRegister reg, static MouseFeatureRegister mouse_reg; compiles fine. but when adding MouseFeatureRegister CvMaskOverlay::mouse_reg("Masking - Polygon"); i do get some link errors. The idea are only for subclasses to register a string in a map with an increasing counter assigned to it. so not using templates – Poul K. Sørensen Nov 28 '11 at 07:50
  • 9
    How do you ensure that `DerivedB::reg` is actually initialized? My understanding is that it may not be constructed at all if no function or object defined in the translation unit `derivedb.cpp`, as per 3.6.2. – musiphil Dec 08 '12 at 05:28
  • And then you can do `return map[some_string]();` is no good if `some_string` does not exist –  Jan 13 '16 at 19:45
  • 2
    Love the self-registration. To compile though I needed a `BaseFactory::map_type * BaseFactory::map = NULL;` in my cpp file. Without this, the linker complained about unknown symbol map. – Sven Aug 15 '16 at 15:53
  • 1
    Unfortunately, this doesn't work. As musiphil already pointed out, `DerivedB::reg` is not initialized if none of its functions or instances is defined in the translation unit `derivedb.cpp`. That means that the class is not registred untill it is actually instantited. Does anybody know a workaround for that? – Tomasito665 Oct 19 '16 at 09:27
  • how is it possible so many people voted up this solution when @musiphil is right and it just doesn't work?? – ihadanny Mar 27 '17 at 16:16
  • 1
    @ihadanny just add a dummy use and you are fine. It's pseudo code. As others have pointed out, definition of `BaseFactory::map` is missing aswell. For a workaround for the static data member issue, see https://stackoverflow.com/questions/401621/best-way-to-for-c-types-to-self-register-in-a-list/401801#401801 – Johannes Schaub - litb Mar 28 '17 at 10:11
  • @JohannesSchaub-litb I've used this solution a few times with self registering classes. I'm now struggling to make the BaseFactory::createInstance method take any number of arguments. I have some object types that don't need any arguments, and some that need a file path and perhaps an additional boolean. I can modify createT to accept typename Args ...., but the reference added to the map knows nothing about that. Anyone successfully done that? Maybe some std::function object that can take and pass variadic args – Hayden Jan 20 '19 at 07:19
  • @JohannesSchaub-litb I was able to figure it out. I used a map to a std::variant of std::function objects, and had to declare the prototype for each new constructor for the variant. Then passed Args... through the templates from the AutoRegister down to createT(Args... args). Might not use it at all though, since it's probably cleaner to let the Factory handle the extra initialization steps and keep all constructors as default, or even private. – Hayden Jan 21 '19 at 22:00
  • On a side note: `static map_type * getMap()` really should not use `new`, that is not thread-safe in this context. A `static` local variable would make more sense, eg: `static map_type * getMap() { static map_type map; return &map; }` Or better: `static map_type & getMap() { static map_type map; return map; }` – Remy Lebeau Aug 23 '21 at 23:05
6

No there isn't. My preferred solution to this problem is to create a dictionary which maps name to creation method. Classes that want to be created like this then register a creation method with the dictionary. This is discussed in some detail in the GoF patterns book.

  • 8
    Anybody care to identify which pattern this is, rather than just point at the book? – josaphatv May 29 '14 at 02:11
  • I think he is referring to the registry pattern. – jiggunjer Jun 18 '15 at 06:33
  • 3
    For those reading this answer now, I believe the answer is referring to using the Factory pattern, an implementation that uses a dictionary to determine which class to instantiate. – Grimeh Jun 29 '15 at 18:42
5

The short answer is you can't. See these SO questions for why:

  1. Why does C++ not have reflection?
  2. How can I add reflection to a C++ application?
Community
  • 1
  • 1
Michael Kristofik
  • 34,290
  • 15
  • 75
  • 125
4

I have answered in another SO question about C++ factories. Please see there if a flexible factory is of interest. I try to describe an old way from ET++ to use macros which has worked great for me.

ET++ was a project to port old MacApp to C++ and X11. In the effort of it Eric Gamma etc started to think about Design Patterns

Community
  • 1
  • 1
epatel
  • 45,805
  • 17
  • 110
  • 144
2

boost::functional has a factory template which is quite flexible: http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html

My preference though is to generate wrapper classes which hide the mapping and object creation mechanism. The common scenario I encounter is the need to map different derived classes of some base class to keys, where the derived classes all have a common constructor signature available. Here is the solution I've come up with so far.

#ifndef GENERIC_FACTORY_HPP_INCLUDED

//BOOST_PP_IS_ITERATING is defined when we are iterating over this header file.
#ifndef BOOST_PP_IS_ITERATING

    //Included headers.
    #include <unordered_map>
    #include <functional>
    #include <boost/preprocessor/iteration/iterate.hpp>
    #include <boost/preprocessor/repetition.hpp>

    //The GENERIC_FACTORY_MAX_ARITY directive controls the number of factory classes which will be generated.
    #ifndef GENERIC_FACTORY_MAX_ARITY
        #define GENERIC_FACTORY_MAX_ARITY 10
    #endif

    //This macro magic generates GENERIC_FACTORY_MAX_ARITY + 1 versions of the GenericFactory class.
    //Each class generated will have a suffix of the number of parameters taken by the derived type constructors.
    #define BOOST_PP_FILENAME_1 "GenericFactory.hpp"
    #define BOOST_PP_ITERATION_LIMITS (0,GENERIC_FACTORY_MAX_ARITY)
    #include BOOST_PP_ITERATE()

    #define GENERIC_FACTORY_HPP_INCLUDED

#else

    #define N BOOST_PP_ITERATION() //This is the Nth iteration of the header file.
    #define GENERIC_FACTORY_APPEND_PLACEHOLDER(z, current, last) BOOST_PP_COMMA() BOOST_PP_CAT(std::placeholders::_, BOOST_PP_ADD(current, 1))

    //This is the class which we are generating multiple times
    template <class KeyType, class BasePointerType BOOST_PP_ENUM_TRAILING_PARAMS(N, typename T)>
    class BOOST_PP_CAT(GenericFactory_, N)
    {
        public:
            typedef BasePointerType result_type;

        public:
            virtual ~BOOST_PP_CAT(GenericFactory_, N)() {}

            //Registers a derived type against a particular key.
            template <class DerivedType>
            void Register(const KeyType& key)
            {
                m_creatorMap[key] = std::bind(&BOOST_PP_CAT(GenericFactory_, N)::CreateImpl<DerivedType>, this BOOST_PP_REPEAT(N, GENERIC_FACTORY_APPEND_PLACEHOLDER, N));
            }

            //Deregisters an existing registration.
            bool Deregister(const KeyType& key)
            {
                return (m_creatorMap.erase(key) == 1);
            }

            //Returns true if the key is registered in this factory, false otherwise.
            bool IsCreatable(const KeyType& key) const
            {
                return (m_creatorMap.count(key) != 0);
            }

            //Creates the derived type associated with key. Throws std::out_of_range if key not found.
            BasePointerType Create(const KeyType& key BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(N,const T,& a)) const
            {
                return m_creatorMap.at(key)(BOOST_PP_ENUM_PARAMS(N,a));
            }

        private:
            //This method performs the creation of the derived type object on the heap.
            template <class DerivedType>
            BasePointerType CreateImpl(BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& a))
            {
                BasePointerType pNewObject(new DerivedType(BOOST_PP_ENUM_PARAMS(N,a)));
                return pNewObject;
            }

        private:
            typedef std::function<BasePointerType (BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& BOOST_PP_INTERCEPT))> CreatorFuncType;
            typedef std::unordered_map<KeyType, CreatorFuncType> CreatorMapType;
            CreatorMapType m_creatorMap;
    };

    #undef N
    #undef GENERIC_FACTORY_APPEND_PLACEHOLDER

#endif // defined(BOOST_PP_IS_ITERATING)
#endif // include guard

I am generally opposed to heavy macro use, but I've made an exception here. The above code generates GENERIC_FACTORY_MAX_ARITY + 1 versions of a class named GenericFactory_N, for each N between 0 and GENERIC_FACTORY_MAX_ARITY inclusive.

Using the generated class templates is easy. Suppose you want a factory to create BaseClass derived objects using a string mapping. Each of the derived objects take 3 integers as constructor parameters.

#include "GenericFactory.hpp"

typedef GenericFactory_3<std::string, std::shared_ptr<BaseClass>, int, int int> factory_type;

factory_type factory;
factory.Register<DerivedClass1>("DerivedType1");
factory.Register<DerivedClass2>("DerivedType2");
factory.Register<DerivedClass3>("DerivedType3");

factory_type::result_type someNewObject1 = factory.Create("DerivedType2", 1, 2, 3);
factory_type::result_type someNewObject2 = factory.Create("DerivedType1", 4, 5, 6);

The GenericFactory_N class destructor is virtual to allow the following.

class SomeBaseFactory : public GenericFactory_2<int, BaseType*, std::string, bool>
{
    public:
        SomeBaseFactory() : GenericFactory_2()
        {
            Register<SomeDerived1>(1);
            Register<SomeDerived2>(2);
        }
}; 

SomeBaseFactory factory;
SomeBaseFactory::result_type someObject = factory.Create(1, "Hi", true);
delete someObject;

Note that this line of the generic factory generator macro

#define BOOST_PP_FILENAME_1 "GenericFactory.hpp"

Assumes the generic factory header file is named GenericFactory.hpp

texta83
  • 590
  • 7
  • 12
2

Detail solution for registering the objects, and accessing them with string names.

common.h:

#ifndef COMMON_H_
#define COMMON_H_


#include<iostream>
#include<string>
#include<iomanip>
#include<map>

using namespace std;
class Base{
public:
    Base(){cout <<"Base constructor\n";}
    virtual ~Base(){cout <<"Base destructor\n";}
};
#endif /* COMMON_H_ */

test1.h:

/*
 * test1.h
 *
 *  Created on: 28-Dec-2015
 *      Author: ravi.prasad
 */

#ifndef TEST1_H_
#define TEST1_H_
#include "common.h"

class test1: public Base{
    int m_a;
    int m_b;
public:
    test1(int a=0, int b=0):m_a(a),m_b(b)
    {
        cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl;
    }
    virtual ~test1(){cout <<"test1 destructor\n";}
};



#endif /* TEST1_H_ */

3. test2.h
#ifndef TEST2_H_
#define TEST2_H_
#include "common.h"

class test2: public Base{
    int m_a;
    int m_b;
public:
    test2(int a=0, int b=0):m_a(a),m_b(b)
    {
        cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl;
    }
    virtual ~test2(){cout <<"test2 destructor\n";}
};


#endif /* TEST2_H_ */

main.cpp:

#include "test1.h"
#include "test2.h"

template<typename T> Base * createInstance(int a, int b) { return new T(a,b); }

typedef std::map<std::string, Base* (*)(int,int)> map_type;

map_type mymap;

int main()
{

    mymap["test1"] = &createInstance<test1>;
    mymap["test2"] = &createInstance<test2>;

     /*for (map_type::iterator it=mymap.begin(); it!=mymap.end(); ++it)
        std::cout << it->first << " => " << it->second(10,20) << '\n';*/

    Base *b = mymap["test1"](10,20);
    Base *b2 = mymap["test2"](30,40);

    return 0;
}

Compile and Run it (Have done this with Eclipse)

Output:

Base constructor
test1 constructor m_a=10m_b=20
Base constructor
test1 constructor m_a=30m_b=40
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
1

Tor Brede Vekterli provides a boost extension that gives exactly the functionality you seek. Currently, it is slightly awkward fitting with current boost libs, but I was able to get it working with 1.48_0 after changing its base namespace.

http://arcticinteractive.com/static/boost/libs/factory/doc/html/factory/factory.html#factory.factory.reference

In answer to those who question why such a thing (as reflection) would be useful for c++ - I use it for interactions between the UI and an engine - the user selects an option in the UI, and the engine takes the UI selection string, and produces an object of the desired type.

The chief benefit of using the framework here (over maintaining a fruit-list somewhere) is that the registering function is in each class's definition (and only requires one line of code calling the registration function per registered class) - as opposed to a file containing the fruit-list, which must be manually added to each time a new class is derived.

I made the factory a static member of my base class.

DAmann
  • 174
  • 7
0

Meaning reflection as in Java. there is some info here: http://msdn.microsoft.com/en-us/library/y0114hz2(VS.80).aspx

Generally speaking, search google for "c++ reflection"

Ido Weinstein
  • 1,176
  • 8
  • 24
0

This is the factory pattern. See wikipedia (and this example). You cannot create a type per se from a string without some egregious hack. Why do you need this?

dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • I need this because I read the strings from a file, and if I have this, then I can have the factory so generic, that it wouldn't have to know anything in order to create the right instance. This is very powerful. – Gal Goldman Feb 24 '09 at 16:14
  • So, are you saying you won't need different class definitions for a Bus and a Car since they are both Vehicles? However, if you do, adding another line shouldn't really be a problem :) The map approach has the same problem -- you update the map contents. The macro thingy works for trivial classes. – dirkgently Feb 24 '09 at 16:18
  • I am saying that in order to CREATE a Bus or a Car in my case, I don't need different definitions, otherwise the Factory design pattern would never be in use. My goal was to have the factory as stupid as it can be. But I see here that there's no escape :-) – Gal Goldman Feb 24 '09 at 16:24
0

Yes, it is possible, without the use of frameworks and macros, just getting the memory address of the class methods and constructors. You can retrieve them from the map generated by the linker, when configured for this action.

visit this site

https://ealaframework.no-ip.org/wiki/page/c.reference

  • As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the [help center](https://stackoverflow.com/help/how-to-answer). –  Nov 23 '21 at 12:18
0

A C++11-style full example:

// Base.h
class Base;
class DerivedA : public Base;
class DerivedB : public Base;

// BaseFactory.h
class BaseFactory
{
public:
    static BaseFactory& get() {
        static BaseFactory singleton;
        return singleton;
    }

    virtual ~BaseFactory() {};

    BaseFactory(const BaseFactory&) = delete;
    BaseFactory(BaseFactory&&) = delete;

    template <class DerivedClass>
    static std::shared_ptr<Base> creator()
    {
        return std::shared_ptr<Base>(new DerivedClass());
    }

    template <class DerivedClass>
    void register_class(const std::string& class_name)
    {
        if (name_to_creator_map.find(class_name) == name_to_creator_map.end())
        {
            std::function<std::shared_ptr<Base>(void)> functor = &BaseFactory::template creator<DerivedClass>;
            name_to_creator_map.emplace(class_name, functor);
        }
    }

    std::shared_ptr<Base> create(const std::string& class_name) const;

private:
    BaseFactory();

    std::map<std::string, std::function<std::shared_ptr<Base>(void)>> name_to_creator_map;
};

// example.cpp using BaseFactory
BaseFactory::get().register_class<DerivedA>("DerivedA");
BaseFactory::get().register_class<DerivedB>("DerivedB");
auto a_obj = BaseFactory::get().create("DerivedA");
auto b_obj = BaseFactory::get().create("DerivedB");
GaloisPlusPlus
  • 403
  • 4
  • 20
0

There is a ready-to-use reflection library https://www.rttr.org/ . You can easily instantiate class by string with it.

struct MyStruct { MyStruct() {}; void func(double) {}; int data; };

RTTR_REGISTRATION
{
    registration::class_<MyStruct>("MyStruct")
         .constructor<>()
         .property("data", &MyStruct::data)
         .method("func", &MyStruct::func);
}


type t = type::get_by_name("MyStruct");
variant var = t.create();