這是一個使用CRTP的例子,它在運行時動態註冊一組生成器(c-startup)。單實例工廠模式用於根據實現中定義的選定算法在運行時動態創建新數據實例。
設計分爲兩部分(命名空間util)。第一部分由所有發生器的僞隨機發生器基礎和模板類和工廠組成。第二部分是爲了實現各種數據生成算法。第二部分中的每個實現類可以拆分爲單獨的文件(在我們的例子中是3個)。
Working example 1
#include <iostream>
#include <string>
#include <map>
#include <memory>
namespace PseudoRandomGeneratorTypes { enum : int { Type1, Type2, Type3 }; }
namespace util {
template<size_t SIZE>
struct __Data {
int a;
};
using Data = __Data<10>;
class PseudoRandomGenerator {
protected:
PseudoRandomGenerator() {}
public:
auto getType() const { return _type; }
virtual Data generate() const = 0;
protected:
int _type;
};
template<int PRGType, typename PRGImpl>
class PRGTmpl : public PseudoRandomGenerator {
public:
static PseudoRandomGenerator* CreatePtr() {
return new PRGImpl();
}
static const int TYPE;
protected:
PRGTmpl() { _type = TYPE; }
};
class PseudoRandomGeneratorFactory {
public:
typedef PseudoRandomGenerator* (*psg)();
static auto get()
{
static PseudoRandomGeneratorFactory fact;
return &fact;
}
auto Register(int id, psg m)
{
_map[id] = m;
return id;
}
auto Create(int id)
{
return _map[id]();
}
private:
PseudoRandomGeneratorFactory() {}
~PseudoRandomGeneratorFactory() {}
std::map<int, psg> _map;
};
template <int arbitaryPRGType, typename arbitaryPRGImpl>
const int PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::TYPE = PseudoRandomGeneratorFactory::get()->Register(arbitaryPRGType, &PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::CreatePtr);
}
namespace util {
class PRGType1 : public PRGTmpl < PseudoRandomGeneratorTypes::Type1, PRGType1 > {
public:
virtual Data generate() const override final { return Data{ 111 }; }
};
template class PRGTmpl < PseudoRandomGeneratorTypes::Type1, PRGType1 >;
class PRGType2: public PRGTmpl < PseudoRandomGeneratorTypes::Type2, PRGType2 > {
public:
virtual Data generate() const override final { return Data{ 222 }; }
};
template class PRGTmpl < PseudoRandomGeneratorTypes::Type2, PRGType2 >;
class PRGType3 : public PRGTmpl < PseudoRandomGeneratorTypes::Type3, PRGType3 > {
public:
virtual Data generate() const override final { return Data{ 333 }; }
};
template class PRGTmpl < PseudoRandomGeneratorTypes::Type3, PRGType3 >;
}
using namespace util;
using namespace std;
int main()
{
auto rng1 = unique_ptr<PseudoRandomGenerator>(PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type1));
auto rng2 = unique_ptr<PseudoRandomGenerator>(PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type2));
auto rng3 = unique_ptr<PseudoRandomGenerator>(PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type3));
cout << rng1->generate().a << endl;
cout << rng2->generate().a << endl;
cout << rng3->generate().a << endl;
}
此外,如果你一次只需要一個實例,並希望不使用堆的CreatePtr()FUNC。可以用以下代替。
static PseudoRandomGenerator* GetPtr() {
static PRGImpl _m;
_m = PRGImpl();
return &_m;
}
專業化變爲:
template <int arbitaryPRGType, typename arbitaryPRGImpl>
const int PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::TYPE = PseudoRandomGeneratorFactory::get()->Register(arbitaryPRGType, &PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::GetPtr);
和使用模式改變爲:
auto rng1 = PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type1);
auto rng2 = PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type2);
auto rng3 = PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type3);
在第二個例子,我們可以使用一個普通的C風格陣列而不是地圖,以避免動態內存分配。
Working example 2
改編自 Dynamically register constructor methods in an AbstractFactory at compile time using C++ templates
你能模板生成器? – Jarod42
你是說在運行時確定SIZE? – zdan
是否可行的做一個潛在的'SIZE硬編碼列表?如果是這樣,有幾個有趣的技巧可以在幕後工作。 –