автоматическая регистрация функции создателя объекта с помощью макроса - PullRequest
4 голосов
/ 26 мая 2011

По сути, я хочу автоматически зарегистрировать функции создателя объекта в фабрике объектов для набора классов, определенных во многих заголовочных файлах.

Ответ на в этом посте , приведенный выше, дает решение- но это не соответствует моим ограничениям.

Я работаю над существующей кодовой базой.Для классов, которые мне нужно зарегистрировать, уже существует макрос, следующий за объявлениями класса, который принимает класс в качестве параметра.Если бы мне удалось расширить существующее определение макроса, чтобы оно также выполняло регистрацию, это сэкономило бы много времени, поскольку не пришлось бы изменять существующий код.

Самое близкое решение, которое мне удалось найтисоздает макрос, который определяет специализацию шаблона для метода, который регистрирует этот объект, затем вызывает ранее определенный метод специализации шаблона - таким образом объединяя в цепочку все вызовы регистра.Затем, когда я хочу зарегистрировать все классы, я просто вызываю последнюю определенную специализацию, и она регистрирует все в обратном порядке появления #include.

Ниже я опубликовал простой рабочий пример, демонстрирующий мое решение.пока что.

Единственное предостережение в том, что у меня нет возможности автоматически отслеживать последний зарегистрированный тип для вызова в цепочке.Поэтому я продолжаю переопределять #define LAST_CHAIN_LINK, чтобы он был самым последним специализированным именем типа.Это означает, что мне нужно было бы добавить две строки # undef / # define после каждого существующего вызова макроса - я бы очень хотел этого избежать.

Основной вопрос: есть ли в приведенном ниже кодеМожно ли определить макрос REGISTER_CHAIN ​​для работы без использования LAST_CHAIN_LINK # undef / # определения кода?

Если бы можно было переопределить токен LAST_CHAIN_LINK внутри метода REGISTER_CHAIN ​​...

Я предполагаю, что какое-то решение возможно с использованием функции препроцессора __COUNTER__, но она недоступна ни на одной из целевых платформ (OS X использует gcc 4.2.x) и, следовательно, не является опцией.

Упрощенный пример (компилируется в 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;
}

пример вывода:

> 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

Ответы [ 2 ]

3 голосов
/ 26 мая 2011

Я нахожу вашу концепцию довольно сложной и не уверена, требуется ли она.С моей точки зрения, вашу проблему можно обойти, добавив следующий код:

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

Надеюсь, это поможет.

С уважением, Мартин

1 голос
/ 27 мая 2011

Используя предложение Мартина / @grundprinzip, я смог решить мою проблему.Мне пришлось немного изменить его подход, чтобы разрешить регистрацию классов в пространствах имен.

Спасибо, Мартин!

Но у меня есть дополнительный вопрос: разве не возможно, что этот компиляторполностью оптимизирует статическую переменную ObjectFactory :: Register :: creator (эквивалентно is_Registered _ ## T в коде @grundprinzip) - потому что ни один код на самом деле не ссылается на это значение?переменная оптимизирует инициализацию ... таким образом нарушая то, чего я надеюсь достичь.

Вот пересмотренный код:

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

дает правильный результат:

> g++ example2.cpp && ./a.out
A::DerivedA constructor
B::DerivedB constructor
C::DerivedC constructor
D::DerivedD constructor
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...