C++ runtime class registration

C++ doesn’t have real pretty introspection capabilities yet, so sometimes you’re stuck rolling your own stuff that kinda sucks. For example, here’s the most convenient method I was able to find to do runtime iteration of all the classes that implement a particular API. As a bonus, you don’t need to add these classes to any header file to work, you just need to implement the class then add AUTO_REGISTER_BASE(classname) to the end of the implementation file.

Update: Here’s a typo-corrected version, along with a more interesting (template function) factory method.

base.h

include <iostream>
#include <string>
#include <vector>

class Base
{
    public:
        virtual void doSomething() = 0;
        virtual std::string getName() = 0;
};

template<class T> Base* base_factory()
{
    return new T;
}

typedef Base* (*base_creator)(void);

class BaseRegistry
{
    private:
        std::vector<base_creator> m_bases;
    public:
        typedef std::vector<base_creator>::iterator iterator;
        static BaseRegistry& get();
        void add(base_creator);
        iterator begin();
        iterator end();
};

class BaseRegistration
{
    public:
        BaseRegistration(base_creator);
};

#define AUTO_REGISTER_BASE(base) \
    BaseRegistration _base_registration_ ## base(&base_factory<base>);

base.cc

#include "base.h"

BaseRegistry& BaseRegistry::get()
{
    static BaseRegistry instance;
    return instance;
}

void BaseRegistry::add(base_creator creator)
{
    m_bases.push_back(creator);
}

BaseRegistry::iterator BaseRegistry::begin()
{
    return m_bases.begin();
}

BaseRegistry::iterator BaseRegistry::end()
{ return m_bases.end(); }

BaseRegistration::BaseRegistration(base_creator creator)
{
    BaseRegistry::get().add(creator);
}

impl1.cc

#include "base.h"

class Impl1 : public Base
{
    std::string getName() { return "impl1"; }
    void doSomething() { std::cout << "impl1 doing something" << std::endl; }
};

AUTO_REGISTER_BASE(Impl1);

main.cc

#include "base.h"

int main()
{
    BaseRegistry& registry(BaseRegistry::get());
    for(BaseRegistry::iterator it = registry.begin(); it != registry.end(); ++it)
    {
        base_creator func = *it;
        Base* _ptr = func();
        std::auto_ptr<Base> ptr(_ptr);
        std::cout << "running object name: " << ptr->getName() <<  std::endl;
        ptr->doSomething();
    }
    return 0;
}

One Comment

  1. Robert Chilton
    Posted July 4, 2006 at 6:28 am | Permalink

    Great post! That’s saved me a couple of hours of trying to figure it out myself

    Rob

One Trackback/Pingback

  1. [...] gives a pointer to meatspace (a blog), where he’s got a solution involving factory methods and a single macro per [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*