Tôi đang tìm một nhà máy trừu tượng cho các mẫu lớp, nơi các lớp đăng ký tự động tại thời gian khởi tạo tĩnh. Đối với các lớp thông thường (không có templated), giải pháp là đủ đơn giản bằng cách sử dụng các thành viên tĩnh. Dưới đây là một ví dụ về một (chứ không phải đơn giản) giải pháp mà làm việc tốt:Đăng ký nhà máy tự động biên dịch các mẫu lớp trong 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;
}
Câu hỏi tôi có là làm thế nào để làm cho nó hoạt khi những thứ tôi muốn đăng ký/tạo được nhiều hơn như:
template <typename T> class Base...
template <typename T> class D1 : public Base<T> ...
ý tưởng tốt nhất mà tôi có thể nghĩ đến là template nhà máy là tốt, cái gì đó như:
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)
Nhưng tôi tự hỏi nếu có một cách tốt hơn, hoặc nếu ai đó đã thực hiện một mô hình như vậy trước đây.
EDIT: xem lại vấn đề này một vài năm sau (và với các mẫu variadic), tôi có thể tiến gần hơn đến những gì tôi muốn bằng cách đơn giản là "đăng ký" chức năng, hay đúng hơn là các tham số mẫu. Nó sẽ trông giống như thế này:
#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;
}
Bây giờ, đây chỉ là một nguyên mẫu đơn giản, vì vậy đừng bao giờ tạo ra sự phức tạp tuyến tính(). Tuy nhiên, thiếu sót chính của thiết kế này là nó không cho phép bất kỳ tham số khởi tạo nào. Lý tưởng nhất, tôi có thể đăng ký không chỉ các lớp mà nhà máy cần tạo, mà còn là các kiểu mà mỗi lớp đưa vào trong hàm khởi tạo của nó, và để tạo() đưa chúng một cách hiệu quả. Có ai từng làm một cái gì đó như thế này trước đây không?
Trái ngược với niềm tin phổ biến, các chức năng của bạn không * tự đăng ký tại * thời gian biên dịch *, mà là bắt đầu chương trình, trước khi 'main()' được gọi. Điều này một phần vì 'registerFunc' không được khai báo là' constexpr', nhưng cũng bởi vì nó không thực sự có thể theo cách bạn đã viết nó. –
Nếu bạn muốn trở nên lạ mắt, bạn có thể tạo một đăng ký người tạo duy nhất, toàn cầu về loại 'std :: map'. Để truy xuất templated theo kiểu 'T' bạn muốn' m [typeid (T)] 'thành' Base (* create)() '. –
Tất nhiên bạn đang đúng về thời gian biên dịch/thời gian init. Xin lỗi vì sự nhầm lẫn. Đối với ý tưởng của bạn cho loại tẩy xoá - nó không phải là xấu. Nó sẽ được khá mặc dù nếu tôi có thể đã ẩn những chi tiết trong một tập tin định nghĩa, nhưng mẫu T ngăn chặn điều đó. – Eitan