我们有
enum Enum {A,B,C,D,E,F,G,H, NumEnums};
class Base {};
template <Enum...> class Thing : public Base {};
和功能
Base* create (std::list<Enum>& input);
是创建与input
对应的类型的对象。例如,
如果input = {A,E,C,G,D};
,则输出应为Thing<A,E,C,G,D>*
类型(让我们忘记这里的排序)。现在我知道input
是在运行期间获得的,但通过搜索,可以非常快速地获得输出。如果Thing
只有一个参数(即input
有size()一个),那么简单
template <int N>
Base* createHelper (const std::list<Enum>& input) {
const Enum En = static_cast<Enum>(N);
if (input.front() == En)
return new Thing<En>;
return createHelper<N+1>(input);
}
template <>
Base* createHelper<NumEnums> (const std::list<Enum>&) {
return nullptr;
}
Base* create (const std::list<Enum>& input) {
return createHelper<0>(input);
}
会做的。我试图将上面的内容推广到任何大小的列表(大小必须在运行时通过上面的类似递归来确定,但这也应该相当快)。但我完全迷失了。所以我试着检查天真方法的结构:
#include <iostream>
#include <list>
#include <type_traits>
#include <typeinfo>
enum Enum {A,B,C,D,E,F,G,H, NumEnums};
class Base {
public:
virtual void print() const = 0;
};
template <Enum...> class Thing : public Base {
virtual void print() const override {std::cout << typeid(*this).name() << '\n';}
};
Base* create (std::list<Enum>& input) {
if (input.front() == A) {
input.pop_front();
if (input.empty())
return new Thing<A>;
else {
if (input.front() == A) {
input.pop_front();
if (input.empty())
return new Thing<A,A>;
else {
// ....
}
}
else if (input.front() == B) {
input.pop_front();
if (input.empty())
return new Thing<A,B>;
else {
// ....
}
}
}
}
else if (input.front() == B) {
// similar
}
// ...
}
int main() {
std::list<Enum> userInput = {A,B};
// Wish to construct an instance of Thing<A,B> (efficiently).
Base* thing = create(userInput);
thing->print(); // Thing<A,B>
}
我想我可以用递归的形式。但我想不出来。我知道一维案例可以概括,但我需要帮助。或者也许有更好的方法完成它?一旦它工作,create
函数返回的时间不应超过几分之一,假设NumEnums
是一个合适的大小而Thing
类只有几个模板参数,而不是数百。
答案 0 :(得分:1)
我不喜欢你这样做的方法,因为它太长而且很乱。
如果我是你,我会在字符串输入和类型之间设置关联映射,然后在从输入正确转换为类型后将类型传递给模板。
不幸的是,因为你无法直接使用C ++中的类型,所以你将被迫自己创建动态系统,而会成为一个心痛的人。我这样做的原始尝试就在这里,但我没有找到一种将变量转换回特定类型的一般方法,而没有粗略的类型检查。
其他语言比C ++更适合这一点 - 作为一种静态语言,C ++的优点和缺点之一就是严格打字。
您可以做的是在输入和变体之间创建一个地图,然后在进行防护演员或检查以确保类型按照您的想法排列后,将变量传递给单数类型变量。当然,如果你没有足够的防守,这就会发生很多错误和崩溃,但这就是这种行为的代价。
以下是一些替代方案:
使用动态语言并将C ++绑定到该语言。这种方法最终适用于大型项目,不仅仅是因为这个原因,但许多应用程序倾向于使用Python或Lua作为脚本语言来加速开发。
重新设计项目以消除对动态行为的依赖。从长远来看,这对设计来说更好(所以我听到了。嗯。)
编辑结果证明,可能有一个可行的解决方案here:
在密钥和类型工厂类之间创建关联数组。
选择后,可以从类型工厂动态分配您可能需要的任何变量(最好使用std::unique_ptr
)。
最终结果可能看起来像这样:
std::unordered_map<std::string, type_allocator> str_to_type;
str_to_type["a"] = type_allocator(int); //where type_allocator derives the type of the class from the input variable.
auto variable = str_to_type[input].allocate();
答案 1 :(得分:1)
对于特定大小,如果计算单个索引,则可以在运行时调度到正确的编译时函数:
template <std::size_t N>
std::unique_ptr<Base> make_thing3()
{
constexpr Enum a2 = Enum(N % NumEnums);
constexpr Enum a1 = Enum((N / NumEnums) % NumEnums);
constexpr Enum a0 = Enum((N / NumEnums / NumEnums) % NumEnums);
return std::make_unique<Thing<a0, a1, a2>>();
}
template <std::size_t... Is>
std::unique_ptr<Base> make_thing3(std::size_t index, std::index_sequence<Is...>)
{
using maker = std::unique_ptr<Base>();
maker* fs[] = {&make_thing3<Is>...};
return fs[index]();
}
std::unique_ptr<Base> make_thing3(const std::array<Enum, 3u>& a)
{
std::size_t index = 0;
for (Enum e : a) {
index *= NumEnums;
index += e;
}
constexpr std::size_t total = NumEnums * NumEnums * NumEnums;
return make_thing3(index, std::make_index_sequence<total>{});
}
注意:我必须更改Enum的大小,并且由于编译器限制而将我的示例从make_thing5
减少到make_thing3
(不确定它是来自网站还是真的限制)< / p>
答案 2 :(得分:0)
此解决方案显示虽然编译时间很长(由于许多模板实例化),但运行时查找是即时的。编译器限制是3个枚举值作为输入。也处理空输入案例(返回类型为Thing<>*
)。
#include <iostream>
#include <list>
#define show(variable) std::cout << #variable << " = " << variable << std::endl;
enum Enum {A,B,C,D,E,F,G,H, NumEnums};
class Base {
public:
virtual void print() const = 0;
};
template <Enum... Es> class Thing : public Base {
virtual void print() const override {
const std::list<int> a = {((std::cout << Es << ' '), 0)...};
std::cout << "\nPack size = " << sizeof...(Es) << '\n';
}
};
template <int N, int Size, Enum... Es>
struct Create {
static Base* execute (std::list<Enum>& input) {
const Enum En = static_cast<Enum>(N);
if (input.front() == En) {
input.pop_front();
return Create<0, Size-1, Es..., En>::execute(input);
}
return Create<N+1, Size, Es...>::execute(input);
}
};
template <int N, Enum... Es>
struct Create<N, 0, Es...> {
static Base* execute (std::list<Enum>&) {return new Thing<Es...>;}
};
template <int Size, Enum... Es>
struct Create<NumEnums, Size, Es...> {
static Base* execute (std::list<Enum>&) {return nullptr;} // This will never be reached
};
template <int Size>
Base* do_create (std::list<Enum>& input) {
if (input.size() == Size)
return Create<0, Size>::execute(input);
return do_create<Size+1>(input);
}
template <>
Base* do_create<4> (std::list<Enum>&) {
std::cout << "Cannot exceed 3 values.\n";
return nullptr;
}
Base* create (std::list<Enum>& input) {
return do_create<0>(input);
}
int main() {
std::list<Enum> input = {E,A,F};
Base* thing = create(input);
thing->print(); // 4 0 5
input = {};
create(input)->print(); // Pack size = 0.
}