Как описано в Этот ответ, вы можете определить производный объект из строки, если вы знаете базовый тип. Я хотел сделать код более гибким, сделав шаблон BaseFactory шаблоном, что приведет к следующему скомпилируемому коду.
#include <iostream>
#include <string>
#include <map>///Reflect and Register
#define MyType(BASE, TYPE) \
static Registerer<BASE, TYPE> reg
#define Register(BASE, TYPE) \
Registerer<BASE, TYPE> TYPE::reg(#TYPE)
template<class B>
class Factory
{
public:
static B* from(const std::string& name)
{
return mapping[name];//should check if its a member
}
static void add(const std::string& name, B* instance)
{
mapping[name] = instance;
}
static std::map<std::string, B*> mapping;
};
template<class B>
std::map<std::string, B*> Factory<B>::mapping = std::map<std::string, B*>();template<class B, class D>
class Registerer
{
public:
Registerer(const std::string& name)
{
Factory<Base>::add(name, new D);//actually add
}
};
///Deduce and Register///Class examples implementing Registration
class Base
{
public:
Base()
{
name = "Base Class";
}
const std::string& getName()
{
return name;
}
protected:
std::string name;
};
class Derived1 : public Base
{
public:
Derived1()
{
name = "I am type 1.\n";
}
MyType(Base, Derived1);
};
Register(Base, Derived1);
class Derived2 : public Base
{
public:
Derived2()
{
name = "I am type 2.\n";
}
MyType(Base, Derived2);
};
Register(Base, Derived2);
///Class examples implementing Registrationint main()
{
std::string typeString1 = "Derived1";
std::string typeString2 = "Derived2";
std::cout << Factory<Base>::from(typeString1)->getName();
std::cout << Factory<Base>::from(typeString2)->getName();
return 0;
}
Однако этот код дает сбой, по-видимому, потому что Factory :: mapping не создается во время, когда вызовы Registerer добавляют. Как описано в Этот ответ, есть решение, которое заключается в добавлении строки
template class Factory<Base>;
которая не решает проблему в Visual Studio, что я и использую. Другая возможность состоит в том, чтобы явно создать новый класс, но в первую очередь нужно было использовать шаблон. Итак, я придумал это:
template<class B>
class Factory
{
public:
static B* from(const std::string& name, B* instance = NULL)
{
static std::map<std::string, B*> mapping;
if(instance != NULL)
{
mapping[name] = instance;
return NULL;
}
else//should check if name is in map first
return mapping[name];
}
};
Это работает, поскольку переменная отображения инициализируется при первом вызове функции. Неудачной частью является проверка if.
Я пытаюсь найти решения, которые:
Не использует одну функцию в Factory для симуляции двух.
Способ зарегистрировать класс одной строкой вместо двух.
Возможность заставить дочерний класс Base зарегистрироваться самостоятельно, чтобы пользователь случайно не зарегистрировал его.
Задача ещё не решена.
Других решений пока нет …