For one of my project, I need the use of variadic templates. Everything works well, except the unpack of the arguments.
Here's the call
Shader<GL_VERTEX_SHADER> vert(vertexShaderSource);
Shader<GL_FRAGMENT_SHADER> frag(fragmentShaderSource);
Program prog(vert, frag);
And the class that causes the problem
class Program
{
public:
template <class... Args>
Program(Args... args) :
program_(glCreateProgram())
{
auto shaders {{args...}};
std::for_each(shaders.begin(), shaders.end(), [this](auto s)
{
std::cout << "glAttachShader\n";
glAttachShader(program_, s.get_shader());
});
}
};
And the error
fatal error: cannot deduce type for variable 'shaders' with type 'auto' from nested initializer list
auto shaders {{args...}};
I tried several things, like
auto shaders = {args...};
auto shaders = {{args...}};
auto shaders {args...};
But nothing works.
And here's the Shader class, just in case
template <GLenum type>
class Shader
{
public:
Shader(std::string const &source)
{
char const *src = source.c_str();
shader_ = glCreateShader(type);
glShaderSource(shader_, 1, &src, NULL);
glCompileShader(shader_);
}
~Shader()
{
glDeleteShader(shader_);
}
inline GLuint get_shader()
{
return shader_;
}
private:
GLuint shader_;
};
Thanks!
This is not really about variadic template unpacking. The problem is when declaring shaders, you need to tell its type. Is it a vector, an array, a tuple, etc.? Since you're using variadic template for that, im guessing shaders can have different types. You would then have to use a tuple.
auto shaders = std::make_tuple(args...);
Iterating on a tuple is not as trivial as an stl container though. Here's an example that use recursion.
template <size_t i = 0,
class Fun,
class Tuple,
size_t N = std::tuple_size<typename std::decay<Tuple>::type>::value,
std::enable_if_t<i >= N>* = nullptr> // if i >= N
void tuple_for_each(Tuple&& t, Fun f) {} // end case
template <size_t i = 0,
class Fun,
class Tuple,
size_t N = std::tuple_size<typename std::decay<Tuple>::type>::value,
std::enable_if_t<i < N>* = nullptr> // if i < N
void tuple_for_each(Tuple&& t, Fun f) {
f(std::get<i>(std::forward<Tuple>(t))); // current iteration
tuple_for_each<i+1>(std::forward<Tuple>(t), std::move(f)); // call next
}
Overall, it's quite intuitive, we start with i = 0, call f() then use recursion to traverse every i until N. C++14 allows you to avoid recursion with std::integer_sequence, which you could search on.
If your wondering about this && nonsense, I would suggest you to read about Universal References. In short, it allows you to prevent copying arguments using references, while making it possible to handle r-values. I would suggest you to do the same for Args... in Program constructor.
Then we can use tuple_for_each to do
tuple_for_each(shaders, [this](auto s) {
std::cout << "glAttachShader\n";
glAttachShader(program_, s.get_shader());
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With