根据用户输入

时间:2015-08-08 00:13:44

标签: c++ templates c++11 variadic

我们有

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类只有几个模板参数,而不是数百。

3 个答案:

答案 0 :(得分:1)

我不喜欢你这样做的方法,因为它太长而且很乱。

如果我是你,我会在字符串输入和类型之间设置关联映射,然后在从输入正确转换为类型后将类型传递给模板。

不幸的是,因为你无法直接使用C ++中的类型,所以你将被迫自己创建动态系统,而成为一个心痛的人。我这样做的原始尝试就在这里,但我没有找到一种将变量转换回特定类型的一般方法,而没有粗略的类型检查。

其他语言比C ++更适合这一点 - 作为一种静态语言,C ++的优点和缺点之一就是严格打字。

您可以做的是在输入和变体之间创建一个地图,然后在进行防护演员或检查以确保类型按照您的想法排列后,将变量传递给单数类型变量。当然,如果你没有足够的防守,这就会发生很多错误和崩溃,但这就是这种行为的代价。

以下是一些替代方案:

  1. 使用动态语言并将C ++绑定到该语言。这种方法最终适用于大型项目,不仅仅是因为这个原因,但许多应用程序倾向于使用Python或Lua作为脚本语言来加速开发。

  2. 重新设计项目以消除对动态行为的依赖。从长远来看,这对设计来说更好(所以我听到了。嗯。)

  3. 编辑结果证明,可能有一个可行的解决方案here

    1. 在密钥和类型工厂类之间创建关联数组。

    2. 选择后,可以从类型工厂动态分配您可能需要的任何变量(最好使用std::unique_ptr)。

    3. 最终结果可能看起来像这样:

      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>{});
}

Live Demo

注意:我必须更改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.
}