简体   繁体   中英

Variadic Template of Templated Classes

I have a struct, defined as follows:

template<typename T>
struct Variable
{
    char *name;

    constexpr Variable(char *setName) : name(setName)
    {

    }
};

I want to create a class whose template parameters are a list of those structs. So far, the closest I can get is this:

template<template<typename TF> Variable First, template<TA...> typename Variable ... Args>
class UniformBuffer
{
};

A UniformBuffer might be declared something like this:

// vec3 and vec4 are types defined elsewhere
UniformBuffer light<Variable<vec3>("location"), Variable<vec4>("rotation"), Variable<float>("intensity")>;

Unfortunately, this doesn't compile, giving me the error "expected 'class' before 'Variable'" (though putting "class" in there simply generates another error, saying that it expected a '>' after 'Variable). Even looking at the various other amusingly-named questions about variadic templates, I don't seem to be able to find the answer for this. What is the correct syntax for what I am trying to do?

It seems you are looking for a specialization:

template<typename First, typename... Args>
class UniformBuffer;

template<typename First, typename... Args>
class UniformBuffer<Variable<First>,Variable<Args>...>
{
};

Live example

You cannot pass objects of Variable as template parameters, because it cannot be deduced at compile time.

Here is an answer which explains that

You are not allowed to pass class instances in as template arguments, since template arguments require compile time resolved things (like constants, function names, types).

It is unfortunate template arguments cannot be string literals.

What you can do is pass those instances into a helper function, from which you can generate a tuple -like object based on the types of those instances.

template <typename T>
struct Variable
{
    typedef T Type;
    const char *name;
    T val;
    constexpr Variable (const char *setName) : name(setName) {}
    operator T () const { return val; }
    operator T & () { return val; }
};

template <typename... V> UniformBuffer<V...> MakeUniformBuffer (V... args) {
    return UniformBuffer<V...>(args...);
}

{
    Variable<vec3> loc("location");
    Variable<vec4> rot("rotation");
    Variable<float> amp("intensity");
    auto ub = MakeUniformBuffer(loc, rot, amp);
...
}

The MakeUniformBuffer passes the instances into the constructor of UniformBuffer . UniformBuffer has to unpack the variable template arguments.

template <typename... V> class UniformBuffer;

template <typename V>
struct UniformBuffer <V> {
    V val;
    UniformBuffer(V v) : val(v) {}
...
};

template <typename V, typename... VV>
struct UniformBuffer<V, VV...> {
    V val;
    UniformBuffer<VV...> ub;
    UniformBuffer(V v, VV... rest) : val(v), ub(rest...) {}
...
};

It is possible to implement set and get methods on UniformBuffer to retrieve buffer elements by name. Below is an illustration of how to implement a get method:

template <typename V>
struct UniformBuffer <V> {
...
    typename V::Type get (const Variable<typename V::Type> &v) {
        if (v.name != val.name) throw v.name;
        return val;
    }
    template <typename R> R get (const Variable<R> &v) {
        throw v.name;
        return R();
    }
};

template <typename V, typename... VV>
struct UniformBuffer<V, VV...> {
...
    typename V::Type get (const Variable<typename V::Type> &v) {
        if (v.name != val.name) return ub.get(v);
        return val;
    }
    template <typename R> R get (const Variable<R> &v) {
        return ub.get(v);
    }
};

{
...
    auto ub = MakeUniformBuffer(loc, rot, amp);
    auto r = ub.get(rot);
...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM