オプション#2と#3はちょうど良いようです。オプション#3を除いて、頂点シェーダとフラグメントシェーダが常に必要とされているので、クラスには、これらの2つの型とセッタをオプションの型だけに使用するコンストラクタを与えます。
しかし、実際にクラスのユーザーにとってもっと簡単にするには、テンプレートの作業によっては1つのコンストラクタしか使用できず、有効なシェーダ引数のセットを任意の順序で渡すことができます以下のような:
#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;
};
私が検証するコンストラクタで4つのstatic_assert
ステートメントを使用しました注:
- シェーダのいかなる種類は複数回渡されません。
- すべての引数はサポートされているシェーダタイプです。 (おそらくそこで使用されている有効なタイプのリストを展開したいと思うでしょう)
- 必要なタイプは
VertexShader
です。
- 必要なタイプは
FragmentShader
です。
namespace ShaderProgramDetail
のクラステンプレートはかなり一般的です。他の場所でそれらを再利用すると思われる場合は、それらを独自のヘッダーファイルに入れ、名前空間の名前をより適切な名前に変更することができます。
上記は有効なC++ 14コードです。あなたがCを使用している場合
template <typename T>
using decay_t = typename std::decay_t<T>::type;
++:
あなたがC++ 11で立ち往生している場合は、独自のdecay_t
をtypename std::decay<X>::type
とすべてのstd::decay_t<X>
を交換するのいずれかの必要がある、または定義します17の場合はShaderProgramDetail::all_of
の代わりにstd::conjunction
を使用できます。コンストラクタの最後の2行をもっと簡単に書くことができます:
init_shader(std::forward<Shaders>(shaders)), ... ;