c++classlambdainitializationclass-factory

C++: How to create a collection of classes at startup


I'm using C++ 11. I have N source files each containing a class that inherits from a common base class. At startup, I want each class to register itself into a collection. The registration is to include 1) data necessary for identifying the purpose of the class, 2) a class factory for creating instances of the class. The number of source files is unknown. What is the pattern for doing this? The solution needs to be cross-platform compatible with Visual Studio 2013, gcc and others.


Solution

  • Using @Pawel's suggestion, I've created an example. The goal is to create a registration process that collects a list of classes, each registration having a class factory and arbitrary data. Specifically, this example registers Mime classes into a std::map. The registration entry holds a vector of content type strings, and a class factory for creating the Mime class.

    In each source file, I use a one liner to register the class. A vector of content types is passed to specify the class's supported types. Every class inherits from a base class named Mime (not shown).

    RegisterTypes<Swagger> swagger(vector<const char *>({ "application/swagger+json" }));
    

    In a header file, define a struct to contain a registration entry. Two initializers are needed, a vector of content types, and the class factory (implemented as a lamda).

    struct Registry
    {
    public:
        Registry() {} // initializer below forces this initializer to be needed
        Registry(vector<const char *> ct, function<Mime*(void)> cf) : contentTypes(ct), classFactory(cf) {}
        vector<const char *> contentTypes;
        function<Mime*(void)> classFactory;
    };
    

    In a header file, extern a std::map which will contain the registrations. Each map entry consists of a key and a registration struct. The actual definition is in a .cpp file (not shown). The registry uses a struct so it can hold multiple values.

    extern std::map<const string, struct Registry> Mimes;
    

    In the header file, define the registration class. Each instantiation of the registration class will create a registration entry in a std::map. In this example, the registration class creates a registration entry consisting of a key (class name), a vector of supported content types, and a class factory. The class factory creates instances of the class and is implemented as a lamda. Every registered class has a common base class of Mime.

    template<class T> class RegisterTypes
    {
    public:
        RegisterTypes(vector<const char *> contentTypes)
        {
            Mimes[typeid(T).name()] = { contentTypes, [](void) -> Mime * { return (Mime *)(new T()); } }; // requires a matching explicit initializer
        }
    };