使用参数组合创建对象

时间:2017-09-23 15:46:58

标签: c++ opengl

我尝试为着色器程序设计类。 着色器程序可能包含多个不同类型的着色器对象。 我不想为每个组合创建不同的consturctor或创建setter,并在此调用成员函数之后创建程序。我试图找到好看的解决方案或使用设计模式,但我不知道好的解决方案。 我有5种不同类型的着色器,并且我总是必须至少向两个着色程序传递着色器(始终是顶点和片段着色器)。我需要着色器才能创建着色器程序,在此之后我不再需要它们了。

选项#1:

ShaderPogram(VertexShader vs, FragmentShader fs);
ShaderPogram(VertexShader vs, GeometryShader geometry, FragmentShader fs);

选项#2:

struct Collection {
 VertexShader vs; GeometryShader gs; FragmentShader fs;
};

ShaderProgram(Collection c);

选项#3:

class ShaderProgram {
public:
 void SetVertexShader(VertexShader vs);
 void SetGeometryShader(GeometryShader gs);
 void SetFragmentShader(FramgentShader fs);
};

也许有人可以提出其他解决方案?

1 个答案:

答案 0 :(得分:0)

你的选择#2和#3看起来很好。除了选项#3之外,因为你说顶点和片段着色器总是需要的,所以我给这个类一个构造函数,它只为那两种类型和setter提供可选类型。

但是如果你真的想要让这个类的用户更容易一些,那么一些模板工作可以使你只使用一个构造函数,并以你喜欢的任何顺序传递任何有效的着色器参数集:

#include <type_traits>

namespace ShaderProgramDetail {
    template <typename Type, typename... TypeList> struct type_in_list;
    template <typename Type>
    struct type_in_list<Type>
        : public std::false_type {};
    template <typename Type, typename... Rest>
    struct type_in_list<Type, Type, Rest...>
        : public std::true_type {};
    template <typename Type, typename First, typename... Rest>
    struct type_in_list<Type, First, Rest...>
        : public std::integral_constant<bool,
            type_in_list<Type, Rest...>::value> {};

    template <typename... Types> struct types_unique;
    template <>
    struct types_unique<> : public std::true_type {};
    template <typename Type1, typename... Rest>
    struct types_unique<Type1, Rest...>
        : public std::integral_constant<bool,
            !type_in_list<Type1, Rest...>::value &&
            types_unique<Rest...>::value> {};

    template <bool... B> struct all_of;
    template <>
    struct all_of<> : public std::true_type {};
    template <bool... B>
    struct all_of<false, B...> : public std::false_type {};
    template <bool... B>
    struct all_of<true, B...>
        : public std::integral_constant<bool, all_of<B...>::value> {};
}

class ShaderProgram
{
public:
    template <typename... Shaders>
    explicit ShaderProgram(Shaders&& ... shaders) {
        using namespace ShaderProgramDetail;
        static_assert(types_unique<std::decay_t<Shaders>...>::value,
            "Cannot provide two shaders of the same type");
        static_assert(all_of<type_in_list<std::decay_t<Shaders>,
            VertexShader, FragmentShader, GeometryShader>
            ::value...>::value,
            "Invalid argument type");
        static_assert(type_in_list<VertexShader, std::decay_t<Shaders>...>::value,
            "Missing VertexShader argument");
        static_assert(type_in_list<FragmentShader, std::decay_t<Shaders>...>::value,
            "Missing FragmentShader argument");
        using int_arr = int[];
        (void) int_arr{ ( init_shader(std::forward<Shaders>(shaders)), 0 )... };
    }
private:
    void init_shader(VertexShader vs) { vertex_shader = std::move(vs); }
    void init_shader(FragmentShader fs) { fragment_shader = std::move(fs); }
    void init_shader(GeometryShader gs) { geometry_shader = std::move(gs); }

    VertexShader vertex_shader;
    FragmentShader fragment_shader;
    GeometryShader geometry_shader;
};

注意我在构造函数中使用了四个static_assert语句来验证:

  • 不会多次传递任何类型的着色器。
  • 每个参数都是受支持的着色器类型。 (您可能希望扩展那里使用的有效类型列表。)
  • 存在所需类型VertexShader
  • 存在所需类型FragmentShader

namespace ShaderProgramDetail中的类模板非常通用。如果您认为可以在其他地方重用它们,可以将它们放在自己的头文件中,并将命名空间重命名为更合适的名称。

以上是有效的C ++ 14代码。

如果您坚持使用C ++ 11,则需要将每个std::decay_t<X>替换为typename std::decay<X>::type,或者定义自己的decay_t

template <typename T>
using decay_t = typename std::decay_t<T>::type;

如果您使用的是C ++ 17,则可以使用std::conjunction代替ShaderProgramDetail::all_of;并且可以更简单地编写构造函数的最后两行:

init_shader(std::forward<Shaders>(shaders)), ... ;