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 なコードでもフリーズしない一番いいマシンを頼む。

[コンパイラ]

  • g++ (GCC) 4.7.0 20111210 (experimental)