Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
156 views
in Technique[技术] by (71.8m points)

c++ - 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()
";   
    ObjectFactory::Register<LAST_CHAIN_LINK>::chain();  
  }

struct DerivedA : public ObjectT<DerivedA> { DerivedA() { std::cout << "DerivedA constructor
"; } };
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
"; } };
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
"; } };
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
"; } };
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
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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
"; } };
REGISTER_CHAIN(DerivedA);

struct DerivedB : public ObjectT<DerivedB> { DerivedB() { std::cout << "DerivedB constructor
"; } };
REGISTER_CHAIN(DerivedB);


struct DerivedC : public ObjectT<DerivedC> { DerivedC() { std::cout << "DerivedC constructor
"; } };
REGISTER_CHAIN(DerivedC);

struct DerivedD : public ObjectT<DerivedD> { DerivedD() { std::cout << "DerivedD constructor
"; } };
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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...