Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

automatic registration of object creator function with a macro

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
like image 693
McKay.CPP Avatar asked Oct 11 '22 22:10

McKay.CPP


1 Answers

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

like image 74
grundprinzip Avatar answered Oct 20 '22 15:10

grundprinzip