Boost Spirit Qi,我有一个解析器,我想映射到一个结构 "dynamically" (意思是参数的顺序不固定)

Boost Spirit Qi, I have a parser that I would like to map onto a struct "dynamically" (meaning order of parameters is not fixed)

所以我努力让 boost::spirit::qi 在我的皮肤下。到目前为止,我的玩具示例是一个解析器,它解析具有以下格式的 Wavefront OBJ material 库:

newmtl ShortBox
Ka  0.6 0.6 0.6
Kd  0.5 0.5 0.5
Ks  0 0 0
d  1
Ns  0
illum 2

但是 material ShortBox 的参数顺序可能会有所不同。我创建了以下成功解析它的语法:

template <typename Iterator>
struct mtllib_grammar : qi::grammar<Iterator, qi::blank_type> {

    mtllib_grammar() : mtllib_grammar::base_type(mtl)
    {
        using qi::char_;
        using qi::double_;
        using qi::int_;


        mtl =
            (
            ("newmtl" >> +(char_ - qi::eol)  >> qi::eol >> *(mtl_details) >> *(mtl))
            | ("#" >> *(qi::char_ - qi::eol) >> qi::eol >> *(mtl))
            );

        mtl_details =
            (
            ("Ka" >> double_ >> double_ >> double_ >> qi::eol >> *(mtl_details))
            | ("Kd" >> double_ >> double_ >> double_ >> qi::eol >> *(mtl_details))
            | ("Ks" >> double_ >> double_ >> double_ >> qi::eol >> *(mtl_details))
            | ("d" >> int_ >> qi::eol >> *(mtl_details))
            | ("Ns" >> int_ >> qi::eol >> *(mtl_details))
            | ("illum" >> int_ >> qi::eol >> *(mtl_details))
            );
    }
    qi::rule<Iterator, qi::blank_type> mtl;
    qi::rule<Iterator, qi::blank_type> mtl_details;
};

现在我想构建一个 std::map<std::string,Material>,其中 Material 定义为:

struct Material {
    Material()
    {
        Ns = 0.0f;
        Ke = glm::vec3(0.0f);
        Kd = glm::vec3(0.0f);
        Ks = glm::vec3(0.0f);
        Ka = glm::vec3(0.0f);
    }
    ~Material() {}
    glm::vec3 Ka;
    glm::vec3 Kd;
    glm::vec3 Ks;
    glm::vec3 Ke;
    float Ns;
};

具有以下融合改编:

BOOST_FUSION_ADAPT_STRUCT(
    glm::vec3,
    (float, x)
    (float, y)
    (float, z)
    )

BOOST_FUSION_ADAPT_STRUCT(
    Material,
    (glm::vec3, Ka)
    (glm::vec3, Kd)
    (glm::vec3, Ks)
    (glm::vec3, Ke)
    (float, Ns)
    )

所以我目前的想法是更改规则 mtl_details,使其 returns 完整 Material 和规则 mtl 返回所述 [=35= 的地图] 键是 newmtl 之后的字符串。但是,我不知道如何使用属性从解析树构建 Material 对象,映射 Ka, Kd, Ks 等的所有命中。在同一个结构上。阅读示例,它们似乎要么隐式映射到与之关联的任何变量,要么只映射到简单值,而不是对象。

感谢 cv_and_he 我找到了解决方案。首先,只有可以在文件中出现的参数应该使用 BOOST_FUSION_ADAPT_STRUCT 映射,所以不要映射 Material::Ke,因为它不能在文件格式中自然出现。

BOOST_FUSION_ADAPT_STRUCT(
    Material,
    (glm::vec3, Ka)
    (glm::vec3, Kd)
    (glm::vec3, Ks)
    (float, Ns)
    )

接下来,我的语法结果如下:

template <typename Iterator>
struct mtllib_grammar : qi::grammar<Iterator, std::map<std::string, Material>(), qi::blank_type> {

    mtllib_grammar() : mtllib_grammar::base_type(mtl)
    {
        using qi::char_;
        using qi::float_;
        using qi::int_;
        using qi::omit;


        mtl =
            (
            *(("newmtl" >> string_rule >> qi::eol >> mtl_details) |
            ("#" >> omit[*(qi::char_ - qi::eol)] >> qi::eol ))
            );

        mtl_details =
            (
            ("Ka" >> glm_rule >> qi::eol) ^
            ("Kd" >> glm_rule >> qi::eol) ^
            ("Ks" >> glm_rule >> qi::eol) ^
            ("d" >> omit[int_] >> qi::eol) ^
            ("Ns" >> int_ >> qi::eol) ^
            ("illum" >> omit[int_] >> qi::eol)
            );

        string_rule = +(char_ - qi::eol);
        glm_rule = float_ >> float_ >> float_;
    }
    qi::rule<Iterator, std::map<std::string, Material>(), qi::blank_type> mtl;

    qi::rule<Iterator, Material(), qi::blank_type> mtl_details;

    qi::rule<Iterator, std::string(), qi::blank_type> string_rule;
    qi::rule<Iterator, glm::vec3(), qi::blank_type> glm_rule;
};

omit 围绕未使用的参数加上注释。否则编译器会因为无法映射到规则中给定的类型而烦恼。