0

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?

Publius
  • 1,184
  • 2
  • 10
  • 27
  • It should be `template class Variable`, not `template class Variable First`. –  Sep 15 '14 at 19:32
  • @remyabel but then how do I access First.name if I do that? I'm trying to make the template parameter a list of Variables. – Publius Sep 15 '14 at 19:33
  • Why do you want to have `Variable` as a template argument? Maybe you're looking for a constructor? – David G Sep 15 '14 at 19:41
  • So you want a variadic template-template parameter? Or variadic list of non-type template parameters (with type `Variable`)? – jrok Sep 15 '14 at 19:41
  • @0x499602D2 Yeah, a constructor would probably work for this. – Publius Sep 15 '14 at 19:46

3 Answers3

3

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

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • I am not looking for a specialization. I am trying to make the template parameter for UniformBuffer a list of object Variable. – Publius Sep 15 '14 at 19:48
  • 2
    @Avi: You **are** looking for a specialization, as this is exactly what will solve your problem. Try it! – Daniel Frey Sep 15 '14 at 19:50
  • @Avi It would also help to show the (possibly hypothetical) syntax you'd wish to work as a result. – jrok Sep 15 '14 at 19:53
  • @DanielFrey No, because I need to access the name of each of the template parameters as well. – Publius Sep 15 '14 at 19:54
  • @Avi I meant an example of *usage* of `UniformBuffer`. What we see now is only an invalid declaration. – jrok Sep 15 '14 at 19:55
  • @jrok Ah, okay, adding that now – Publius Sep 15 '14 at 19:55
  • @Avi: You have access to them, through `First` and `Args...` (or `Variable` when you need that). What am I missing? – Daniel Frey Sep 15 '14 at 19:55
  • @DanielFrey I need access to the name property of the Variable class. The template parameters aren't supposed to be typenames, they're supposed to be Variables. – Publius Sep 15 '14 at 19:58
  • @Avi You pass the objects to `UniformBuffer`'s constructor. –  Sep 15 '14 at 19:59
  • @remyabel then don't I just have to figure out this same syntax, but put it before the constructor instead of the class declaration? – Publius Sep 15 '14 at 20:03
  • @remyabel or I guess it would work if I could recurse through variadic function parameters while recursing through variadic template parameters of a different type. – Publius Sep 15 '14 at 20:06
1

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

Here is an answer which explains that

Community
  • 1
  • 1
Rafal Mielniczuk
  • 1,322
  • 7
  • 11
  • Is there no way to make it deducible at compile time through careful use of constexpr or something? It seems like if I type "Variable("foo")", then that gives the compiler everything it needs. – Publius Sep 15 '14 at 19:45
  • 1
    But you are invoking here the constructor, and that cannot be done at compile time – Rafal Mielniczuk Sep 15 '14 at 20:22
  • C++11 doesn't have constexpr constructors? http://stackoverflow.com/questions/21557755/constexpr-constructor-with-compile-time-validation – Publius Sep 15 '14 at 22:23
  • Still it needs to allocate memory for the object – Rafal Mielniczuk Sep 16 '14 at 12:28
  • then I'm kind of confused as to the purpose of constexpr constructors. – Publius Sep 17 '14 at 03:28
  • 1
    Looks like it allows you tu use that object in constexpr functions. [here](http://en.wikipedia.org/wiki/C%2B%2B11#constexpr_.E2.80.93_Generalized_constant_expressions) you can read more about it. – Rafal Mielniczuk Sep 17 '14 at 09:04
1

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);
...
}
jxh
  • 69,070
  • 8
  • 110
  • 193