Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scalable automatic class registration in C++

Automatic class registration in C++ is a common task, and a commonly asked question here on StackOverflow:

Register an object creator in object factory

Somehow register my classes in a list

automatic registration of object creator function with a macro

c++ automatic factory registration of derived types

The basic objective is to register classes automatically with some registry or factory so that it can do some work with each class later.

This is a well-established technique, used by libraries like (for example) Google Test (http://code.google.com/p/googletest), which automatically registers subclasses of the Test class so that each test can be instantiated automatically and run during test execution.

Registration can be accomplished by instantiating a static Registrar class whose constructor does the registration, or by clever use of the CRTP and putting the registration code in the base class constructor, or whatever you like (the links above offer several different possible techniques).

However, when I implement any of these techniques I find they scale very poorly. If I have 10,000 TEST macro invocations in Google Test, compilation and linking grind to a halt (MSVC 2010) and binary size explodes. If I implement this another way, using 10,000 subclasses with static registrars, I see the same behavior.

For instance, consider the simplified example:

#include <iostream>
#include <string>

class Base {

    public:

        Base( const std::string& Name_ ) : Name( Name_ ) { ; }
       ~Base() { ; }

        virtual std::string GetName() const { return Name; }
        virtual void DoSomething() = 0;

    private:

        std::string Name;

};

class Registry {

    public:

        static Registry& GetInstance() {
            static Registry* Instance = new Registry();
            return *Instance;
        }

        void Register( const Base* b ) {
            std::cout << "Registered class " << b->GetName() << std::endl;
        }

    private:

        Registry()  { ; }
       ~Registry()  { ; }

};

class Registrar {

    public:

        Registrar( const Base* b ) {
            Registry::GetInstance().Register( b );
        }

       ~Registrar() { }

};


#define  REGISTER( CLASS )                                          \
    class CLASS : public Base {                                     \
        public:                                                     \
            CLASS( const std::string& Name ) : Base( Name ) { ; }   \
            virtual void DoSomething();                             \
        private:                                                    \
            static Registrar m_Registrar;                           \
    };                                                              \
    Registrar CLASS::m_Registrar( new CLASS( #CLASS ) );            \
    void CLASS::DoSomething()


int main( int argc, char** argv )
{
    return 0;
}


REGISTER( Class1 )
{
    std::cout << "Doing something in Class1" << std::endl;
}

REGISTER( Class2 )
{
    std::cout << "Doing something in Class2" << std::endl;
}

[...]

with a total of 10000 autogenerated REGISTER calls.

Is there a fundamental reason why this won't scale well? Will the compiler just choke on 10000 classes? Under MSVC 2010 compilation of the above takes almost two minutes on a fairly fast machine and produces a binary over 5 MB in size. If I do similar with Google Test I see the same result.

like image 853
DSII Avatar asked Dec 11 '12 16:12

DSII


1 Answers

Writing Java code in C++ rarely works well. All those heap allocations are probably what's killing performance (as they would in Java, but Java startup is so slow that nobody would notice). Use static objects, and don't put a Registrar object into each generated class; that's just a waste of time and space.

like image 155
Pete Becker Avatar answered Oct 14 '22 06:10

Pete Becker