Я ищу абстрактную фабрику для шаблонов классов, где классы регистрируются автоматически во время статической инициализации.Для обычных (не шаблонных) классов решение достаточно простое, используя статические члены.Вот пример (довольно упрощенного) решения, которое работает просто отлично:
#include <cassert>
#include <iostream>
class Base {
public:
virtual size_t id() const = 0;
virtual const char* name() const = 0;
virtual ~Base() {}
};
typedef Base* (*CreateFunc)(void);
class SimpleFactory {
private:
static const size_t NELEM = 2;
static size_t id_;
static CreateFunc creators_[NELEM];
public:
static size_t registerFunc(CreateFunc creator) {
assert(id_ < NELEM);
assert(creator);
creators_[id_] = creator;
return id_++;
}
static Base* create(size_t id) { assert(id < NELEM); return (creators_[id])(); }
};
size_t SimpleFactory::id_ = 0;
CreateFunc SimpleFactory::creators_[NELEM];
class D1 : public Base {
private:
static Base* create() { return new D1; }
static const size_t id_;
public:
size_t id() const { return id_; }
const char* name() const { return "D1"; }
};
const size_t D1::id_ = SimpleFactory::registerFunc(&create);
class D2 : public Base {
private:
static Base* create() { return new D2; }
static const size_t id_;
public:
size_t id() const { return id_; }
const char* name() const { return "D2"; }
};
const size_t D2::id_ = SimpleFactory::registerFunc(&create);
int main() {
Base* b1 = SimpleFactory::create(0);
Base* b2 = SimpleFactory::create(1);
std::cout << "b1 name: " << b1->name() << "\tid: " << b1->id() << "\n";
std::cout << "b2 name: " << b2->name() << "\tid: " << b2->id() << "\n";
delete b1;
delete b2;
return 0;
}
У меня есть вопрос, как заставить его работать, когда материал, который я хочу зарегистрировать / создать, больше похож на:
template <typename T> class Base...
template <typename T> class D1 : public Base<T> ...
Лучшая идея, которую я могу придумать, - это также создать шаблон фабрики, например:
template <typename T>
class SimpleFactory {
private:
static const size_t NELEM = 2;
static size_t id_;
typedef Base<T>* Creator;
static Creator creators_[NELEM];
...(the rest remains largely the same)
Но мне интересно, есть ли лучший способ или кто-то реализовал такойшаблон до.
РЕДАКТИРОВАТЬ: вернувшись к этой проблеме несколько лет спустя (и с помощью шаблонов с переменными значениями), я могу значительно приблизиться к тому, что хочу, просто «зарегистрировав» функции или, скорее, классы, в качестве параметров шаблона дляфабрика.Это выглядело бы примерно так:
#include <cassert>
struct Base {};
struct A : public Base {
A() { std::cout << "A" << std::endl; }
};
struct B : public Base {
B() { std::cout << "B" << std::endl; }
};
struct C : public Base {
C() { std::cout << "C" << std::endl; }
};
struct D : public Base {
D() { std::cout << "D" << std::endl; }
};
namespace {
template <class Head>
std::unique_ptr<Base>
createAux(unsigned id)
{
assert(id == 0);
return std::make_unique<Head>();
}
template <class Head, class Second, class... Tail>
std::unique_ptr<Base>
createAux(unsigned id)
{
if (id == 0) {
return std::make_unique<Head>();
} else {
return createAux<Second, Tail...>(id - 1);
}
}
}
template <class... Types>
class LetterFactory {
public:
std::unique_ptr<Base>
create(unsigned id) const
{
static_assert(sizeof...(Types) > 0, "Need at least one type for factory");
assert(id < sizeof...(Types));
return createAux<Types...>(id);
}
};
int main() {
LetterFactory<A, B, C, D> fac;
fac.create(3);
return 0;
}
Теперь это просто упрощенный прототип, так что не забывайте о линейной сложности create ().Однако основным недостатком этого проекта является то, что он не учитывает никаких параметров конструктора.В идеале я мог бы зарегистрировать не только классы, которые нужны фабрике для создания, но также и типы, которые каждый класс принимает в своем конструкторе, и позволить create () принимать их по-разному.Кто-нибудь когда-нибудь делал что-то подобное раньше?