在模板特化

时间:2016-05-24 13:57:20

标签: c++ templates metaprogramming

以下代码显示了我使用的设计。我创建了一个封装模板类的包装类。包装器的方法允许使用开关选择我想要的专业化:

#include <memory>
#include <string>

/* Interface
 */
struct IFoo
{
    virtual void lol(void) = 0;
};

/* Template Specialization
 */
template<std::size_t N>
class Foo : public IFoo
{
    void lol(void) {}
};

/* Wrapper for the template
 */
class FooWrapper : public IFoo
{
    std::unique_ptr<IFoo>   mfoo;

    public:
    void setSize(std::size_t size)
    {
        switch (size) // how to 'simulate' this switch comportement
                      // with min/max constexpr variables ?
        {
            case 1u:
                mfoo = std::make_unique< Foo<1u> >();
                break ;
            case 2u:
                mfoo = std::make_unique< Foo<2u> >();
                break ;
            case 3u:
                mfoo = std::make_unique< Foo<3u> >();
                break ;
            default:
                throw std::runtime_error(std::to_string(size) + " not supported.");
        }
    }

    FooWrapper(std::size_t size)
    {
        this->setSize(size);
    }

    void lol(void)
    {
        mfoo->lol();
    }
};

int main(void)
{
    FooWrapper  a(3u);  // ok
    a.setSize(2u);      // ok
    a.setSize(0u);      // will throw an exception at runtime

    return EXIT_SUCCESS;
}

是否可以使用相同的比例,但使用最小和最大constexpr值,以及在范围内循环并为每个值执行良好模板的交换机的自动版本?

编辑:我正在搜索运行时解决方案,因为用户必须通过GUI选择setSize的参数。

3 个答案:

答案 0 :(得分:3)

与所有模板元编程问题一样,涉及index_sequence。在这种情况下,为index_sequence上的模板参数建立一个Foo的值,并迭代它们。为简单起见,这里的版本使用0作为最小值,4作为最大值:

template <std::size_t... Is>
std::unique_ptr<IFoo> create(std::size_t N, std::index_sequence<Is...> ) {
    std::unique_ptr<IFoo> mfoo;

    using swallow = int[];        
    (void)swallow{0,
        (void(N == Is ? mfoo = std::make_unique<Foo<Is>>() : mfoo), 0)...
    };

    return mfoo;
}

std::unique_ptr<IFoo> create(std::size_t N) {
    return create(N, std::make_index_sequence<4>{} );
}

create(N)会为您提供Foo<N>(如果0 <= N < 4)或未设置unique_ptr。如果它什么都不给你,你可以扔掉:

void setSize(std::size_t size) {
    auto new_foo = create(size); // or call with index_sequence
    if (!new_foo) {
        // throw
    }

    mfoo = std::move(new_foo);
}

我将生成index_sequence<1, 2, 3>作为练习。

答案 1 :(得分:1)

您可以创建一个init-time函数表,这样该表的每个成员都对应于上面的一个case语句。然后setSize可以使用此表。通过使用constexpr min和max参数,您可以使用模板特化指定此表的边界,并使用创建Foo对象的'maker'函数的模板实例填充它。

这是函数表代码的声明(全部在FooWrapper的私有部分中):

template<unsigned i>
static std::unique_ptr<IFoo> fooMaker()
{
    return std::make_unique< Foo<i> >();
}

static constexpr unsigned FOO_MIN = 1;
static constexpr unsigned FOO_MAX = 3;

using FooMakerFn = std::unique_ptr<IFoo>();

template<unsigned min>
static std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1>& initFooFnTable(std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1>& fnTable);

static std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1> fooFnTable;

以下是函数表创建的定义,包括终止案例的专门化:

std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1> FooWrapper::fooFnTable = FooWrapper::initFooFnTable<FooWrapper::FOO_MIN>(FooWrapper::fooFnTable);

template<unsigned min>
std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& FooWrapper::initFooFnTable(std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FOO_MIN-1>& fnTable)
{
    fnTable[min - FOO_MIN] = FooWrapper::fooMaker<min>;
    return initFooFnTable<min+1>(fnTable);
}

template<>
std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& FooWrapper::initFooFnTable<FooWrapper::FOO_MAX>(std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& fnTable)
{
    fnTable[FOO_MAX - FOO_MIN] = FooWrapper::fooMaker<FooWrapper::FOO_MAX>;
    return fnTable;
}

这是完整的代码:

#include <memory>
#include <string>
#include <iostream>

/* Interface
 */
struct IFoo
{
    virtual void lol(void) = 0;
};

/* Template Specialization
 */
template<std::size_t N>
class Foo : public IFoo
{
    void lol(void) 
    {
        std::cout << "Lol: " << N << std::endl;
    }
};

/* Wrapper for the template
 */
class FooWrapper : public IFoo
{
    std::unique_ptr<IFoo>   mfoo;

    public:
    void setSize(std::size_t size)
    {
        if(size >= FOO_MIN && size <= FOO_MAX)
            mfoo = fooFnTable[size - FOO_MIN]();
        else
            throw std::runtime_error(std::to_string(size) + " not supported.");
    }

    FooWrapper(std::size_t size)
    {
        this->setSize(size);
    }

    void lol(void)
    {
        mfoo->lol();
    }

private:

    template<unsigned i>
    static std::unique_ptr<IFoo> fooMaker()
    {
        return std::make_unique< Foo<i> >();
    }

    static constexpr unsigned FOO_MIN = 1;
    static constexpr unsigned FOO_MAX = 3;

    using FooMakerFn = std::unique_ptr<IFoo>();

    template<unsigned min>
    static std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1>& initFooFnTable(std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1>& fnTable);

    static std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1> fooFnTable;
};

std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1> FooWrapper::fooFnTable = FooWrapper::initFooFnTable<FooWrapper::FOO_MIN>(FooWrapper::fooFnTable);

template<unsigned min>
std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& FooWrapper::initFooFnTable(std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FOO_MIN-1>& fnTable)
{
    fnTable[min - FOO_MIN] = FooWrapper::fooMaker<min>;
    return initFooFnTable<min+1>(fnTable);
}

template<>
std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& FooWrapper::initFooFnTable<FooWrapper::FOO_MAX>(std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& fnTable)
{
    fnTable[FOO_MAX - FOO_MIN] = FooWrapper::fooMaker<FooWrapper::FOO_MAX>;
    return fnTable;
}

int main(void)
{
    FooWrapper  a(3u);  // ok
    a.setSize(2u);      // ok
    a.setSize(0u);      // will throw an exception at runtime

    a.lol();

    return EXIT_SUCCESS;
}

答案 2 :(得分:0)

我怀疑这是可能的。 setSize可以constexpr,因为它是具有非平凡析构函数的类的非静态成员。

因为无法创建constexpr,所以代码中的所有模板参数都必须进行硬编码。