template<class T>
struct named_factory {
const char* name;
std::function<std::unique_ptr<T>()> factory;
};
struct find_factory {
using is_transparent=std::true_type;
struct named {
const char* str;
template<class T>
named(named_factory<T> const& f):str(f.name) {}
named(const char* name):str(name) {}
};
bool operator()(named lhs, named rhs) {
return strcmp(lhs.str, rhs.str)<0;
}
};
#define MAKE_STR2(X) #X
#define MAKE_STR(X) MAKE_STR2(X)
#define FACTORY(X,...) \
named_factory<__VA_ARGS__>{\
MAKE_STR(X),\
[]{\
return std::make_unique<X>()\
}\
}
现在我们可以:
std::set<named_factory<foo>, find_factory> factories = {
FACTORY(P2_26, foo),
FACTORY(P4_30, foo),
// ...
};
在你做的代码中:
bool add_module_by_name( const char* name ) {
auto it = factories.find(name);
if (it == factories.end()) return false;
auto module = it->factory();
if (!module) return false;
add_module( module.release() );
return true;
}
这是一个数据驱动的设计。对正确类型的搜索是在对数时间内完成的,而不是像您的代码那样线性。您可以将其替换为 unordered_map 而不是 set。
然而,如果你的类型名称是在编译时确定的,你可以做得更好。 (即,如果您在呼叫站点有硬编码"P2_26")。
template<class T>
struct tag_t { using type=T; constexpr tag_t(){} };
template<class T>
constexpr tag_t<T> tag{};
template<class T>
void add_module( tag_t<T> ) {
// ...
add_module( new T() );
}
现在您可以add_module(tag<P2_26>) 并跳过冗长的 if/else 语句。
我们甚至可以通过这个隐藏外部add_module的实现:
// in cpp file:
void add_module_impl( std::function< std::unique_ptr<foo>() > maker ) {
// ...
add_module( maker().release() );
}
// in h file:
void add_module_impl( std::function< std::unique_ptr<foo>() > maker );
template<class T>
void add_module( tag_t<T> t ) {
add_module_impl([]{ return std::make_unique<T>(); });
}
再一次,我们可以add_module(tag<P4_30>) 并且它可以正常工作。