class 名称的 C++ 数组
C++ array of class names
我正在构建一个可以通过添加新的 class
来扩展的 c++ 框架
我想找到一种方法来简化新 classes 的扩展。
我当前的代码如下:
class Base {
public:
virtual void doxxx() {...}
};
class X: public Base {
public:
static bool isMe(int i) { return i == 1; }
};
class Y: public Base {
public:
static bool isMe(int i) { return i == 2; }
};
class Factory {
public:
static std::unique_ptr<Base> getObject(int i) {
if (X::isMe(i)) return std::make_unique<X>();
if (Y::isMe(i)) return std::make_unique<Y>();
throw ....
}
};
还必须为每个新 class 添加一个新的 if 语句。
现在我想找到一种方法来重写我的工厂 class(使用元编程),可以通过调用添加方法来添加新的 class
工厂 class 看起来像下面的伪代码:
class Factory
{
public:
static std::unique_ptr<Base> getObject(int i) {
for X in classNames:
if (X::isMe(i)) return std::make_unique<X>();
throw ....
}
static void add() {...}
static classNames[];...
};
Factory::add(X);
Factory::add(Y);
。
.
这样的事情可能吗?
非常感谢
您可以执行如下操作:
template <typename ... Ts>
class Factory {
public:
static std::unique_ptr<Base> createObject(int i) {
if (i < sizeof...(Ts)) {
static const std::function<std::unique_ptr<Base>()> fs[] = {
[](){ return std::make_unique<Ts>();}...
};
return fs[i]();
}
throw std::runtime_error("Invalid arg");
}
};
用法为:
using MyFactory = Factory<X, Y /*, ...*/>;
auto x = MyFactory::createObject(0);
auto y = MyFactory::createObject(1);
如果您想要运行时注册,您可以改为:
class Factory {
public:
static std::unique_ptr<Base> createObject(int i) {
auto it = builders.find(i);
if (it == builders.end()) {
throw std::runtime_error("Invalid arg");
}
return it->second();
}
template <typename T>
void Register()
{
builders.emplace(T::Id, [](){ return std::make_unique<T>();});
}
private:
std::map<int, std::function<std::unique_ptr<Base>()>> builders;
};
用法为:
class X: public Base {
public:
static constexpr int id = 1;
};
class Y: public Base {
public:
static constexpr int id = 2;
};
和
Factory factory;
factory.register<X>();
factory.register<Y>();
auto x = factory.createObject(1);
auto y = factory.createObject(Y::id);
你可以只拥有一个模板函数:
#include <memory>
class Base {
public:
virtual void doxxx() { /* ... */ };
};
template<int i>
std::unique_ptr<Base> getObject();
#define REGISTER_CLASS(CLS, ID) \
template<> \
std::unique_ptr<Base> getObject<ID>() { return std::unique_ptr<Base>(new CLS()); }
class X1: public Base {
public:
// ...
};
REGISTER_CLASS(X1, 1)
class X2: public Base {
public:
// ...
};
REGISTER_CLASS(X2, 2)
int main()
{
auto obj1 = getObject<1>(); // Makes X1
auto obj2 = getObject<2>(); // Makes X2
return 0;
}
然而,这只允许 class 编译时已知的 id 值。
这是一个带有 add
函数和 map
来存储不同类型的版本。
class Base {
public:
virtual void doxxx() {...}
};
class X: public Base {
public:
static int isMe = 1;
};
class Y: public Base {
public:
static int isMe = 2;
};
class Factory {
public:
static std::unique_ptr<Base> getObject(int i) {
if (classID.find(i) != classID.end())
return classID[i]();
throw ....
}
template <typename C>
static void add() {
classID[C::isMe] = [](){ return std::make_unique<C>(); };
}
static std::map<int, std::function<std::unique_ptr<Base>()>> classID;
};
// Called like this
Factory::add<X>();
Factory::add<Y>();
你可以这样做:
#include <type_traits>
#include <utility>
#include <memory>
#include <functional>
#include <stdexcept>
class I {
public:
virtual ~I(); // Always give a polymorphic class a virtual dtor!
virtual void doxxx();
};
enum class I_Key {
X = 1,
Y
/*...*/
};
struct I_Key_Order {
bool operator()(I_Key k1, I_Key k2) const
{
using U = std::underlying_type_t<I_Key>;
return static_cast<U>(k1) < static_cast<U>(k2);
}
};
class I_Factory {
public:
using Generator = std::function<std::unique_ptr<I>()>;
static std::unique_ptr<I> getObject(I_Key key) const;
static void add(I_Key key, Generator gen);
template <class T>
class AutoRegister {
public:
AutoRegister() {
auto generator = []() -> std::unique_ptr<I>
{ return std::make_unique<T>(); };
add(T::class_key, std::move(generator));
}
AutoRegister(const AutoRegister&) = delete;
AutoRegister& operator=(const AutoRegister&) = delete;
};
private:
using GenMapT = std::map<I_Key, Generator, I_Key_Order>;
static GenMapT& m_generators();
};
inline std::unique_ptr<I> I_Factory::getObject(I_Key key)
{
auto& gen_map = m_generators();
auto iter = gen_map.find(key);
if (iter != gen_map.end())
return iter->second();
throw std::invalid_argument("unknown I_Factory key");
}
inline void I_Factory::add(I_Key key, Generator gen)
{
m_generators().emplace(key, std::move(gen));
}
I_Factory::GenMapT& I_Factory::m_generators()
{
static GenMapT generators;
return generators;
}
class X : public I {
public:
static constexpr I_Key class_key = I_Key::X;
static const I_Factory::AutoRegister<X> _reg;
};
class Y : public I {
public:
static constexpr I_Key class_key = I_Key::Y;
static const I_Factory::AutoRegister<Y> _reg;
};
请注意,除了 I_Factory::add()
函数外,我还设置了第二种更简单的方法来添加 class C
的生成器:定义类型为 I_Factory::AutoRegister<C>
,只要 C::class_key
是有效的枚举数。如果对象是静态 class 成员,则添加基本上会在程序开始时发生。这仅在 class 具有 public 默认构造函数时有效,因此如果需要不同的东西,可能仍会使用 add
函数。
C++14 解决方案。
必填headers:
#include <memory>
#include <iostream>
A class 找不到索引时抛出
class ClassNotIndexed{};
帮助程序 class 帮助在索引和请求的类型之间创建绑定:
template< typename ClassType, typename IndexType, IndexType idx >
class ClassIndexer
{};
out factory 的主要模板class
template <typename... >
class ClassSelector;
工厂的基本案例 class。找到索引后创建unique_pointer,否则抛出异常:
template<typename BaseClass,
typename ClassType, typename IndexType, IndexType idx,
template<typename,typename,IndexType> typename T>
class ClassSelector<BaseClass, T<ClassType,IndexType, idx>>
{
public:
constexpr static
std::unique_ptr<BaseClass> getObject(IndexType i)
{
if (i == idx)
return std::make_unique<ClassType>();
else
throw ClassNotIndexed();
}
};
采用许多索引 classes 的可变参数模板。
如果第一个 class 的索引匹配,则 make_unique_pointer 否则检查下一个索引 class:
template< typename BaseClass,
typename ClassType, typename IndexType, IndexType idx,
template< typename , typename, IndexType > typename T,
typename... Ts >
class ClassSelector<BaseClass, T<ClassType,IndexType, idx>, Ts... > : ClassSelector< BaseClass, Ts ... >
{
using Base = ClassSelector< BaseClass, Ts ... >;
public:
constexpr static
std::unique_ptr<BaseClass> getObject(IndexType i)
{
if (i == idx)
return std::make_unique<ClassType>();
else
return Base::getObject( i );
}
};
示例:
class Base
{
public:
virtual void doxxx() {}
};
class X : public Base
{
public:
//static bool isMe( int i ) { return i == 1; }
void doxxx() override {std::cout << "I'm X !!\n";}
};
class Y : public Base
{
public:
//static bool isMe( int i ) { return i == 2; }
void doxxx() override {std::cout << "I'm Y !!\n";}
};
int main()
{
using Factory = ClassSelector<
Base,
ClassIndexer< X, int, 1 >,
ClassIndexer< Y, int, 2 >
>;
Factory::getObject( 1 )->doxxx();
Factory::getObject( 2 )->doxxx();
int choose;
std::cin >> choose;
Factory::getObject( choose )->doxxx();
return 0;
}
我正在构建一个可以通过添加新的 class
来扩展的 c++ 框架我想找到一种方法来简化新 classes 的扩展。
我当前的代码如下:
class Base {
public:
virtual void doxxx() {...}
};
class X: public Base {
public:
static bool isMe(int i) { return i == 1; }
};
class Y: public Base {
public:
static bool isMe(int i) { return i == 2; }
};
class Factory {
public:
static std::unique_ptr<Base> getObject(int i) {
if (X::isMe(i)) return std::make_unique<X>();
if (Y::isMe(i)) return std::make_unique<Y>();
throw ....
}
};
还必须为每个新 class 添加一个新的 if 语句。
现在我想找到一种方法来重写我的工厂 class(使用元编程),可以通过调用添加方法来添加新的 class 工厂 class 看起来像下面的伪代码:
class Factory
{
public:
static std::unique_ptr<Base> getObject(int i) {
for X in classNames:
if (X::isMe(i)) return std::make_unique<X>();
throw ....
}
static void add() {...}
static classNames[];...
};
Factory::add(X);
Factory::add(Y);
。 .
这样的事情可能吗? 非常感谢
您可以执行如下操作:
template <typename ... Ts>
class Factory {
public:
static std::unique_ptr<Base> createObject(int i) {
if (i < sizeof...(Ts)) {
static const std::function<std::unique_ptr<Base>()> fs[] = {
[](){ return std::make_unique<Ts>();}...
};
return fs[i]();
}
throw std::runtime_error("Invalid arg");
}
};
用法为:
using MyFactory = Factory<X, Y /*, ...*/>;
auto x = MyFactory::createObject(0);
auto y = MyFactory::createObject(1);
如果您想要运行时注册,您可以改为:
class Factory {
public:
static std::unique_ptr<Base> createObject(int i) {
auto it = builders.find(i);
if (it == builders.end()) {
throw std::runtime_error("Invalid arg");
}
return it->second();
}
template <typename T>
void Register()
{
builders.emplace(T::Id, [](){ return std::make_unique<T>();});
}
private:
std::map<int, std::function<std::unique_ptr<Base>()>> builders;
};
用法为:
class X: public Base {
public:
static constexpr int id = 1;
};
class Y: public Base {
public:
static constexpr int id = 2;
};
和
Factory factory;
factory.register<X>();
factory.register<Y>();
auto x = factory.createObject(1);
auto y = factory.createObject(Y::id);
你可以只拥有一个模板函数:
#include <memory>
class Base {
public:
virtual void doxxx() { /* ... */ };
};
template<int i>
std::unique_ptr<Base> getObject();
#define REGISTER_CLASS(CLS, ID) \
template<> \
std::unique_ptr<Base> getObject<ID>() { return std::unique_ptr<Base>(new CLS()); }
class X1: public Base {
public:
// ...
};
REGISTER_CLASS(X1, 1)
class X2: public Base {
public:
// ...
};
REGISTER_CLASS(X2, 2)
int main()
{
auto obj1 = getObject<1>(); // Makes X1
auto obj2 = getObject<2>(); // Makes X2
return 0;
}
然而,这只允许 class 编译时已知的 id 值。
这是一个带有 add
函数和 map
来存储不同类型的版本。
class Base {
public:
virtual void doxxx() {...}
};
class X: public Base {
public:
static int isMe = 1;
};
class Y: public Base {
public:
static int isMe = 2;
};
class Factory {
public:
static std::unique_ptr<Base> getObject(int i) {
if (classID.find(i) != classID.end())
return classID[i]();
throw ....
}
template <typename C>
static void add() {
classID[C::isMe] = [](){ return std::make_unique<C>(); };
}
static std::map<int, std::function<std::unique_ptr<Base>()>> classID;
};
// Called like this
Factory::add<X>();
Factory::add<Y>();
你可以这样做:
#include <type_traits>
#include <utility>
#include <memory>
#include <functional>
#include <stdexcept>
class I {
public:
virtual ~I(); // Always give a polymorphic class a virtual dtor!
virtual void doxxx();
};
enum class I_Key {
X = 1,
Y
/*...*/
};
struct I_Key_Order {
bool operator()(I_Key k1, I_Key k2) const
{
using U = std::underlying_type_t<I_Key>;
return static_cast<U>(k1) < static_cast<U>(k2);
}
};
class I_Factory {
public:
using Generator = std::function<std::unique_ptr<I>()>;
static std::unique_ptr<I> getObject(I_Key key) const;
static void add(I_Key key, Generator gen);
template <class T>
class AutoRegister {
public:
AutoRegister() {
auto generator = []() -> std::unique_ptr<I>
{ return std::make_unique<T>(); };
add(T::class_key, std::move(generator));
}
AutoRegister(const AutoRegister&) = delete;
AutoRegister& operator=(const AutoRegister&) = delete;
};
private:
using GenMapT = std::map<I_Key, Generator, I_Key_Order>;
static GenMapT& m_generators();
};
inline std::unique_ptr<I> I_Factory::getObject(I_Key key)
{
auto& gen_map = m_generators();
auto iter = gen_map.find(key);
if (iter != gen_map.end())
return iter->second();
throw std::invalid_argument("unknown I_Factory key");
}
inline void I_Factory::add(I_Key key, Generator gen)
{
m_generators().emplace(key, std::move(gen));
}
I_Factory::GenMapT& I_Factory::m_generators()
{
static GenMapT generators;
return generators;
}
class X : public I {
public:
static constexpr I_Key class_key = I_Key::X;
static const I_Factory::AutoRegister<X> _reg;
};
class Y : public I {
public:
static constexpr I_Key class_key = I_Key::Y;
static const I_Factory::AutoRegister<Y> _reg;
};
请注意,除了 I_Factory::add()
函数外,我还设置了第二种更简单的方法来添加 class C
的生成器:定义类型为 I_Factory::AutoRegister<C>
,只要 C::class_key
是有效的枚举数。如果对象是静态 class 成员,则添加基本上会在程序开始时发生。这仅在 class 具有 public 默认构造函数时有效,因此如果需要不同的东西,可能仍会使用 add
函数。
C++14 解决方案。
必填headers:
#include <memory>
#include <iostream>
A class 找不到索引时抛出
class ClassNotIndexed{};
帮助程序 class 帮助在索引和请求的类型之间创建绑定:
template< typename ClassType, typename IndexType, IndexType idx >
class ClassIndexer
{};
out factory 的主要模板class
template <typename... >
class ClassSelector;
工厂的基本案例 class。找到索引后创建unique_pointer,否则抛出异常:
template<typename BaseClass,
typename ClassType, typename IndexType, IndexType idx,
template<typename,typename,IndexType> typename T>
class ClassSelector<BaseClass, T<ClassType,IndexType, idx>>
{
public:
constexpr static
std::unique_ptr<BaseClass> getObject(IndexType i)
{
if (i == idx)
return std::make_unique<ClassType>();
else
throw ClassNotIndexed();
}
};
采用许多索引 classes 的可变参数模板。 如果第一个 class 的索引匹配,则 make_unique_pointer 否则检查下一个索引 class:
template< typename BaseClass,
typename ClassType, typename IndexType, IndexType idx,
template< typename , typename, IndexType > typename T,
typename... Ts >
class ClassSelector<BaseClass, T<ClassType,IndexType, idx>, Ts... > : ClassSelector< BaseClass, Ts ... >
{
using Base = ClassSelector< BaseClass, Ts ... >;
public:
constexpr static
std::unique_ptr<BaseClass> getObject(IndexType i)
{
if (i == idx)
return std::make_unique<ClassType>();
else
return Base::getObject( i );
}
};
示例:
class Base
{
public:
virtual void doxxx() {}
};
class X : public Base
{
public:
//static bool isMe( int i ) { return i == 1; }
void doxxx() override {std::cout << "I'm X !!\n";}
};
class Y : public Base
{
public:
//static bool isMe( int i ) { return i == 2; }
void doxxx() override {std::cout << "I'm Y !!\n";}
};
int main()
{
using Factory = ClassSelector<
Base,
ClassIndexer< X, int, 1 >,
ClassIndexer< Y, int, 2 >
>;
Factory::getObject( 1 )->doxxx();
Factory::getObject( 2 )->doxxx();
int choose;
std::cin >> choose;
Factory::getObject( choose )->doxxx();
return 0;
}