使用宏自动注册对象创建函数

2022-01-11 00:00:00 macros templates c++

基本上,我想通过对象工厂为跨许多头文件定义的一堆类自动注册对象创建函数.

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.

我能想到的最接近的解决方案是创建一个宏,该宏定义注册该对象的方法的模板特化,然后调用先前定义的模板特化方法――从而链接所有注册调用.然后,当我想注册所有类时,我只需调用最近定义的特化,它会以 #include 外观的相反顺序注册所有内容.

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.

唯一需要注意的是,我无法自动跟踪要在链中调用的最后一个注册类型.所以我不断将#define LAST_CHAIN_LINK 重新定义为最近专门的类型名.这意味着我必须在每个现有的宏调用之后添加两行#undef/#define――我真的很想避免这种情况.

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.

主要问题:在下面的代码中,是否有任何方法可以在不使用 LAST_CHAIN_LINK #undef/#define 代码的情况下定义 REGISTER_CHAIN 宏?

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?

如果可以在 REGISTER_CHAIN 方法中重新定义 LAST_CHAIN_LINK 令牌...

If only it were possible to redefine the LAST_CHAIN_LINK token inside the REGISTER_CHAIN method...

我的猜测是使用 __COUNTER__ 预处理器功能可以实现一些解决方案,但这在其中一个目标平台(使用 gcc 4.2.x 的 OS X)上不可用,因此不是一个选项.

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.

简化示例(在 GNU C++ 4.4.3 上编译):

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;
}

示例输出:

> 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
"; } };
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;
}

我希望这会有所帮助.

最好的问候,马丁

相关文章