Sprout.Weed を使ってコンパイル時に shader をパース
元々これがやりたくてコンパイル時パーサが欲しかったんですが、今のレベルでどれほどのものなのか試してみました。
Shader はここの example から拝借
[ソース]
#define SPROUT_CONFIG_SUPPORT_TEMPORARY_CONTAINER_ITERATION #include <sprout/weed.hpp> template<typename Parsed> struct parsed_holder{ constexpr operator bool() const{ return parsed.success(); } constexpr decltype(std::declval<Parsed>().attr()) attr(){ return parsed.attr(); } template<typename T> constexpr bool operator ==(T&& t) const{ return parsed.attr() == t; } Parsed parsed; }; template<typename Char, std::size_t N, typename Parser> constexpr auto parse(Char const(&str)[N], Parser&& parser) ->parsed_holder<decltype( sprout::weed::parse(str, str+N-1, sprout::forward<Parser>(parser)) )>{ return { sprout::weed::parse(str, str+N-1, sprout::forward<Parser>(parser)) }; } template<typename Item> constexpr auto make_variable_parser_impl(Item&& item) ->decltype(*sprout::weed::omit[sprout::weed::char_ - item] >> item){ return *sprout::weed::omit[sprout::weed::char_ - item] >> item; } template< typename Attr, std::size_t AttrN, typename Type, std::size_t TypeN, typename Name, std::size_t NameN > constexpr auto make_variable_parser( Attr const(&attr)[AttrN], Type const(&type)[TypeN], Name const(&name)[NameN] )->decltype(make_variable_parser_impl( attr >> +sprout::weed::omit[sprout::weed::space] >> type >> *sprout::weed::omit[ sprout::weed::char_-((+sprout::weed::omit[sprout::weed::space]|','|';') >> name) ] >> (+sprout::weed::omit[sprout::weed::space]|','|';') >> name >> *sprout::weed::omit[sprout::weed::space] >> (sprout::weed::char_(',')|sprout::weed::char_(';')) )){ return make_variable_parser_impl( attr >> +sprout::weed::omit[sprout::weed::space] >> type >> *sprout::weed::omit[ sprout::weed::char_-((+sprout::weed::omit[sprout::weed::space]|','|';') >> name) ] >> (+sprout::weed::omit[sprout::weed::space]|','|';') >> name >> *sprout::weed::omit[sprout::weed::space] >> (sprout::weed::char_(',')|sprout::weed::char_(';')) ); } int main(){ namespace w = sprout::weed; static constexpr char vert[] = R"delimiter( #version 330\n uniform mat4 ProjectionMatrix, CameraMatrix, ModelMatrix; in vec4 Position; in vec3 Normal; in vec2 TexCoord; out vec3 vertNormal; out vec3 vertLight; out vec2 vertTexCoord; uniform vec3 LightPos; )delimiter"; // main 関数まで書いてしまうとコンパイラが死んでしまうので… // void main(void) // { // vertNormal = ( // ModelMatrix * // vec4(Normal, 0.0) // ).xyz; // vertLight = ( // vec4(LightPos, 0.0)- // ModelMatrix * Position // ).xyz; // vertTexCoord = TexCoord; // gl_Position = // ProjectionMatrix * // CameraMatrix * // ModelMatrix * // Position; // } static_assert(parse(vert, make_variable_parser("uniform", "mat4", "ProjectionMatrix")), ""); static_assert(parse(vert, make_variable_parser("uniform", "mat4", "CameraMatrix")), ""); static_assert(parse(vert, make_variable_parser("uniform", "mat4", "ModelMatrix")), ""); static_assert(parse(vert, make_variable_parser("in", "vec4", "Position")), ""); static_assert(parse(vert, make_variable_parser("in", "vec3", "Normal")), ""); static_assert(parse(vert, make_variable_parser("in", "vec2", "TexCoord")), ""); static_assert(parse(vert, make_variable_parser("out", "vec3", "vertNormal")), ""); static_assert(parse(vert, make_variable_parser("out", "vec3", "vertLight")), ""); static_assert(parse(vert, make_variable_parser("out", "vec2", "vertTexCoord")), ""); static_assert(parse(vert, make_variable_parser("uniform", "vec3", "LightPos")), ""); static_assert(!parse(vert, make_variable_parser("in", "vec3", "Position")), ""); static_assert(!parse(vert, make_variable_parser("in", "vec4", "Posision")), ""); static_assert(!parse(vert, make_variable_parser("out", "vec4", "Position")), ""); return 0; }
とりあえず、こんな感じで変数をパースしてみました。
Shader にそのパラメータが存在するのかどうかをチェックしているだけなので、変数名を取得して〜みたいな処理は行なっていません。
実際に使用する場合のイメージとしては、shader パラメータを設定する場合に存在しない名前ならばコンパイルエラーにして欲しいって感じですかねぇ。
上記のコードの場合は、1つのスコープ内で全ての処理が完結しているので問題はないんですが、実際に使用することを考えると Shader を誰が保持するのか、どうやってエラーにするのかなどなど考える事がまだありますね…。
最初に全体をパースしてしまって、データを保持しておいた方がいいかも。
とりあえず、C++11 なコードでもフリーズしない一番いいマシンを頼む。