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)