从variadic模板解包参数

时间:2016-09-19 19:48:56

标签: c++ templates

对于我的一个项目,我需要使用可变参数模板。一切都运作良好,除了参数的解包。

这是电话

Shader<GL_VERTEX_SHADER> vert(vertexShaderSource);
Shader<GL_FRAGMENT_SHADER> frag(fragmentShaderSource);
Program prog(vert, frag);

导致问题的类

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());
            });
        }
};

错误

fatal error: cannot deduce type for variable 'shaders' with type 'auto' from nested initializer list
    auto shaders {{args...}};

我尝试了几件事,比如

auto shaders = {args...};
auto shaders = {{args...}};
auto shaders {args...};    

但没有任何作用。

以下是Shader课程,以防万一

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_;
};

谢谢!

4 个答案:

答案 0 :(得分:6)

这不是关于可变参数模板解包的。问题是在声明着色器时,您需要告诉它的类型。它是矢量,数组,元组等吗? 由于您正在使用可变参数模板,因此我猜测着色器可以有不同的类型。然后你必须使用一个元组。

auto shaders = std::make_tuple(args...);

迭代元组并不像stl容器那样微不足道。这是一个使用递归的例子。

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
}

总的来说,它非常直观,我们从i = 0开始,调用f()然后使用递归来遍历每个i直到N. C ++ 14允许你避免使用std :: integer_sequence递归,你可以搜索

如果你想知道这个和&amp;&amp;胡说八道,我建议你阅读Universal References。简而言之,它允许您使用引用来防止复制参数,同时可以处理r值。我建议你为程序构造函数中的Args做同样的事情。

然后我们可以使用tuple_for_each来做

tuple_for_each(shaders, [this](auto s) {
  std::cout << "glAttachShader\n";
  glAttachShader(program_, s.get_shader());
});

答案 1 :(得分:3)

您可以使用数组来解包参数,而不是使用std::for_each

诀窍是使用list-initialization来解包参数,调用lambda,并使用,运算符来删除值并使用0代替。

struct Program {
    template <class... Args>
    Program(Args... args) : program_(glCreateProgram()) {  
        int unpack[] = {([this](auto& shader){
            std::cout << "glAttachShader\n";
            glAttachShader(program_, s.get_shader());
        }(args), 0)..., 0};

        // use this to silent the warning
        static_cast<void>(unpack);
    }
};

你可以通过发送lambda而不是硬编码来概括这个“包foreach”:

template<typename F, typename... Args>
void pack_foreach(F f, Args&&... args) {
    int unpack[] = {(f(std::forward<Args>(args)), 0)..., 0};

    static_cast<void>(unpack);
}

离开你的代码:

struct Program {
    template <class... Args>
    Program(Args... args) : program_(glCreateProgram()) {  
        pack_foreach([this](auto shader){
            std::cout << "glAttachShader\n";
            glAttachShader(program_, s.get_shader());
        }, args...);
    }
};

答案 2 :(得分:2)

另一种方式 - 将复杂的东西放入自己的私有方法中:

class Program
{
    template<class ShaderTuple, std::size_t...Is>
    void attach_shaders(const ShaderTuple& shaders, std::index_sequence<Is...>)
    {
        using expand = int[];
        void(expand
        {
            0,
            (
             std::cout << "glAttachShader\n",
             glAttachShader(program_, std::get<Is>(shaders).get_shader()),
             0
             )...
        });

    }

public:
    template <class... Shaders>
    Program(Shaders&&... args)
    : program_(glCreateProgram())
    {
        attach_shaders(std::make_tuple(std::forward<Shaders>(args)...),
                       std::make_index_sequence<sizeof...(Shaders)>());
    }
};

答案 3 :(得分:0)

基于环路的范围在这种情况下非常适合 另外,尝试在lambda中直接使用auto来避免额外的副本 这是一个最小的工作示例:

#include<iostream>

class Program {
public:
    template <class... Args>
    Program(Args... args) {
        for(auto &&arg: { args... }) {
            std::cout << "do whatever you want" << std::endl;
        }
    }
};

int main() {
    Program p{42, 0, 1};
}

以下是您的代码审核:

class Program {
public:
    template <class... Args>
    Program(Args... args) : program_(glCreateProgram()) {
        for(auto &&s: { args... }) {
            std::cout << "glAttachShader" << std::endl;
            glAttachShader(program_, s.get_shader());
        }
    }
};