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;
}
Great post! That’s saved me a couple of hours of trying to figure it out myself
Rob
Pingback: C++ Runtime Types: Find All Classes Derived From a Base Class (2) « Sambal
Pingback: Sambal : C++ Runtime Types: Find All Classes Derived From a Base Class (2)