根据用户输入创建特定类型的对象

Creating an object of specific type based on user input

我们有

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 函数到 return 的时间应该不会超过几分之一秒,假设 NumEnums 是一个合适的大小并且 Thing [=33] =] 只有几个模板参数,而不是数百个。

编辑: 事实证明,可能有一个可行的解决方案 here

  1. 在您的键和类型工厂之间创建一个关联数组class。

  2. 选择类型工厂后,动态分配您可能需要的任何变量(最好使用 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();

对于特定大小,如果计算单个 index,您可以在运行时分派到正确的编译时函数:

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(不确定它是来自网站还是真正的限制)

这个解决方案表明,虽然编译时间很长(由于模板实例化很多),运行-时间查找是即时的。编译器限制是 3 个枚举值作为输入。空输入案例也被处理(return 类型为 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.
}