Автоматическая регистрация фабрики шаблонов классов во время компиляции в C++

Я ищу абстрактную фабрику для шаблонов классов, где классы регистрируются автоматически во время статической инициализации. Для обычных (нешаблонных) классов решение достаточно простое - использование статических членов. Вот пример (довольно упрощенного) решения, которое отлично работает:

#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)

Но мне интересно, есть ли лучший способ, или если кто-то уже реализовал такой шаблон.

EDIT: возвращаясь к этой проблеме несколько лет спустя (и с вариативными шаблонами), я могу гораздо ближе подойти к тому, что хочу, просто "зарегистрировав" функции, или, скорее, классы, как параметры шаблона для фабрики. Это будет выглядеть примерно так:

#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() принимать их переменным образом. Кто-нибудь уже делал что-то подобное?

9
задан Eitan 12 October 2015 в 22:18
поделиться