automatic registration of object creator function with a macro

后端 未结 2 1614
遥遥无期
遥遥无期 2020-12-11 08:30

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 t

相关标签:
2条回答
  • 2020-12-11 09:04

    Using Martin/ @grundprinzip 's suggestion I've been able to solve my problem. I had to modify his approach a bit to allow registration of classes in namespaces.

    Thanks Martin!

    But, I have a follow-up question now: Isn't it possible the that compiler will entirely optimize out the static ObjectFactory::Register::creator variable (equivalent to is_Registered_##T in @grundprinzip 's code) -- because no code actually references this value?

    If so, then optimizing out the variable will optimize out the initialization...thus breaking what I'm hoping to achieve.

    Here is the revised code:

    #include <map>
    #include <string>
    #include <iostream>
    
    struct Object{ virtual ~Object() {} }; // base type for all objects
    
    struct ObjectFactory {
      static Object* create(const std::string& id) { // creates an object from a string
        const Creators_t::const_iterator iter = static_creators().find(id);
        return iter == static_creators().end() ? 0 : (*iter->second)(); // if found, execute the creator function pointer
      }
    
     private:
      typedef Object* Creator_t(); // function pointer to create Object
      typedef std::map<std::string, Creator_t*> Creators_t; // map from id to creator
      static Creators_t& static_creators() { static Creators_t s_creators; return s_creators; } // static instance of map
      template<class T = int> struct Register {
        static Object* create() { return new T(); };
        static Creator_t* init_creator(const std::string& id) { return static_creators()[id] = create; }
        static Creator_t* creator;
      };
    };
    
    #define REGISTER_TYPE(T, STR) template<> ObjectFactory::Creator_t* ObjectFactory::Register<T>::creator = ObjectFactory::Register<T>::init_creator(STR)
    
    namespace A { struct DerivedA : public Object { DerivedA() { std::cout << "A::DerivedA constructor\n"; } }; }
    REGISTER_TYPE(A::DerivedA, "A");
    
    namespace B { struct DerivedB : public Object { DerivedB() { std::cout << "B::DerivedB constructor\n"; } }; }
    REGISTER_TYPE(B::DerivedB, "Bee");
    
    namespace C { struct DerivedC : public Object { DerivedC() { std::cout << "C::DerivedC constructor\n"; } }; }
    REGISTER_TYPE(C::DerivedC, "sea");
    
    namespace D { struct DerivedD : public Object { DerivedD() { std::cout << "D::DerivedD constructor\n"; } }; }
    REGISTER_TYPE(D::DerivedD, "DEE");
    
    int main(void)
    {
      delete ObjectFactory::create("A");
      delete ObjectFactory::create("Bee");
      delete ObjectFactory::create("sea");
      delete ObjectFactory::create("DEE");
      return 0;
    }
    

    produces the correct result:

    > g++ example2.cpp && ./a.out
    A::DerivedA constructor
    B::DerivedB constructor
    C::DerivedC constructor
    D::DerivedD constructor
    
    0 讨论(0)
  • 2020-12-11 09:09

    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

    0 讨论(0)
提交回复
热议问题