Basically, I want to automatically register object creator functions with an object factory for a bunch of classes defined across many header files.
The top answer to this post, provides a solution -- but it doesn't fit my constraints.
I'm working on an existing code base. For the classes I need to register, there is already a macro following the class declarations that takes the class as a parameter. If I were able to extend the existing macro definition to also do registration, then it would save a lot time because no existing code would have to be changed.
The closest solution I've been able to come up with is creating a macro that defines a template specialization of a method that registers that object, then calls the previously defined template specialization method -- thus chaining all the register calls. Then, when I want to register all classes, I just call the most recently defined specialization and it registers everything in reverse order of #include appearance.
Below, I've posted a simple working example that shows my solution thus far.
The only caveat is that I have no way of automatically keeping track of the last registered type to call in the chain. So I keep redefining the #define LAST_CHAIN_LINK to be the most recently specialized typename. This means that I'd have to add two lines of #undef/#define after every existing macro call -- I'd really like to avoid that.
The main question: In the code below, is there any way to define the REGISTER_CHAIN macro to work without using the LAST_CHAIN_LINK #undef/#define code too?
If only it were possible to redefine the LAST_CHAIN_LINK token inside the REGISTER_CHAIN method...
My guess is some solution is possible using the __COUNTER__
preprocessor feature, but that is not available on one of the target platforms (OS X using gcc 4.2.x) and thus not an option.
Simplified example (compiles on GNU C++ 4.4.3):
#include <map>
#include <string>
#include <iostream>
struct Object{ virtual ~Object() {} }; // base type for all objects
// provide a simple create function to derived classes
template<class T> struct ObjectT : public Object {
static Object* create() { return new T(); }
};
struct ObjectFactory {
// pass in creator function pointer to register it to id
static Object* create(const std::string& id, Object* (*creator)() = 0) {
static std::map<std::string, Object* (*)()> creators;
return creator && (creators[id] = creator) ? 0 : creators.find(id) != creators.end() ? (*creators.find(id)->second)() : 0;
}
template<class T = int> struct Register { static void chain() {} };
};
#define LAST_CHAIN_LINK // empty to start
#define REGISTER_CHAIN(T) \
template<> void ObjectFactory::Register<T>::chain() \
{ \
ObjectFactory::create(#T, T::create); \
std::cout << "Register<" << #T << ">::chain()\n"; \
ObjectFactory::Register<LAST_CHAIN_LINK>::chain(); \
}
struct DerivedA : public ObjectT<DerivedA> { DerivedA() { std::cout << "DerivedA constructor\n"; } };
REGISTER_CHAIN(DerivedA);
// Can these next two lines be eliminated or folded into REGISTER_CHAIN?
#undef LAST_CHAIN_LINK
#define LAST_CHAIN_LINK DerivedA
struct DerivedB : public ObjectT<DerivedB> { DerivedB() { std::cout << "DerivedB constructor\n"; } };
REGISTER_CHAIN(DerivedB);
// Can these next two lines be eliminated or folded into REGISTER_CHAIN?
#undef LAST_CHAIN_LINK
#define LAST_CHAIN_LINK DerivedB
struct DerivedC : public ObjectT<DerivedC> { DerivedC() { std::cout << "DerivedC constructor\n"; } };
REGISTER_CHAIN(DerivedC);
// Can these next two lines be eliminated or folded into REGISTER_CHAIN?
#undef LAST_CHAIN_LINK
#define LAST_CHAIN_LINK DerivedC
struct DerivedD : public ObjectT<DerivedD> { DerivedD() { std::cout << "DerivedD constructor\n"; } };
REGISTER_CHAIN(DerivedD);
// Can these next two lines be eliminated or folded into REGISTER_CHAIN?
#undef LAST_CHAIN_LINK
#define LAST_CHAIN_LINK DerivedD
int main(void)
{
// Call last link in the register chain to register all object creators
ObjectFactory::Register<LAST_CHAIN_LINK>::chain();
delete ObjectFactory::create("DerivedA");
delete ObjectFactory::create("DerivedB");
delete ObjectFactory::create("DerivedC");
delete ObjectFactory::create("DerivedD");
return 0;
}
example output:
> g++ example.cpp && ./a.out
Register<DerivedD>::chain()
Register<DerivedC>::chain()
Register<DerivedB>::chain()
Register<DerivedA>::chain()
DerivedA constructor
DerivedB constructor
DerivedC constructor
DerivedD constructor
I find your concept pretty complicated and I'm not sure if it's required. From my point of view your problem can be circumvented when adding the following code:
#include <iostream>
#include <map>
#include <string>
struct Object{}; // Value Object
// provide a simple create function to derived classes
template<class T> struct ObjectT : public Object {
static Object* create() { return new T(); }
};
struct ObjectFactory {
std::map<std::string, Object* (*)()> creators_factory;
static ObjectFactory* instance()
{
static ObjectFactory* __self = NULL;
if (__self == NULL)
__self = new ObjectFactory();
return __self;
}
template <class T> bool reg(const std::string& id, Object* (*creator)() )
{
creators_factory[id] = creator;
return true;
}
// pass in creator function pointer to register it to id
static Object* create(const std::string& id) {
return instance()->creators_factory[id]();
}
};
#define REGISTER_CHAIN(T) bool isRegistered_##T = ObjectFactory::instance()->reg<T>(#T, T::create)
struct DerivedA : public ObjectT<DerivedA> { DerivedA() { std::cout << "DerivedA constructor\n"; } };
REGISTER_CHAIN(DerivedA);
struct DerivedB : public ObjectT<DerivedB> { DerivedB() { std::cout << "DerivedB constructor\n"; } };
REGISTER_CHAIN(DerivedB);
struct DerivedC : public ObjectT<DerivedC> { DerivedC() { std::cout << "DerivedC constructor\n"; } };
REGISTER_CHAIN(DerivedC);
struct DerivedD : public ObjectT<DerivedD> { DerivedD() { std::cout << "DerivedD constructor\n"; } };
REGISTER_CHAIN(DerivedD);
int main(void)
{
// Call last link in the register chain to register all object creators
//ObjectFactory::Register<LAST_CHAIN_LINK>::chain();
delete ObjectFactory::create("DerivedA");
delete ObjectFactory::create("DerivedB");
delete ObjectFactory::create("DerivedC");
delete ObjectFactory::create("DerivedD");
return 0;
}
I hope this helps.
Best regards, Martin
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With