6

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.

Community
  • 1
  • 1
DSII
  • 429
  • 6
  • 15
  • 2
    Please pay attention to the difference between a `class` and an `object`. This code does **not** register classes (nor should it), it registers **objects**. – Pete Becker Dec 11 '12 at 16:36
  • Thanks Pete. For my purpose, as for Google Test's, there'll be a one-to-one mapping so I can choose a technique that registers either, but your point is well-taken. – DSII Dec 11 '12 at 16:38
  • 1
    No, without a **great** deal of effort, you can't register classes. – Pete Becker Dec 11 '12 at 16:40

1 Answers1

1

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.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • Thanks Pete -- but I've tried that, and it doesn't help. It might shave 15 seconds off a 2 minute compile time, and 200 kB off a 5 MB binary. Runtime performance isn't the issue; any of the implementations I've tried run fast enough. – DSII Dec 11 '12 at 17:08
  • Just to be clear, you're basically suggesting having single static instantiations of each derived class, and having the base class constructor do the registration, right? I have indeed tried that, and it behaves identically. – DSII Dec 11 '12 at 17:10
  • Oh, you're talking about compile time? No, I don't think there's much you can do there. Automatic registration depends on construction of objects, and that means creating an object for everything that needs to be registered. – Pete Becker Dec 11 '12 at 17:46
  • Compile time and binary size both. Why are any of these techniques so much more expensive in compile time and binary size than defining and calling 10000 free functions? And if the expense is unavoidable, is there an alternate technique for automatic registration that will scale better? – DSII Dec 11 '12 at 17:56
  • You're creating 10000 objects of 10000 different types; each has its data elements, a vtable (in a typical implementation), and a constructor. For each object, the compiler adds an entry to the program's startup table for the constructor call. That all adds up. Most compilers have a way of registering a function to be called at startup; you might be able to avoid this overhead by writing a function or set of functions to do the registration and using those non-portable extensions to have them called during startup. – Pete Becker Dec 11 '12 at 18:03
  • Even supposing I could solve the problem of calling arbitrary registration code at startup, I would still have to have an automatically-generated list of subclasses. Is there a way to create that at compile-time maybe? – DSII Dec 11 '12 at 18:06
  • I don't see how, off the top of my head. This is typically a job for an external tool that generates the appropriate code. – Pete Becker Dec 11 '12 at 18:12
  • OK, I will accept this answer as a result of the discussion in the comments that led to the conclusion: there's nothing I can do about it. – DSII Dec 12 '12 at 13:27