使用 boost::spirit::qi (error_invalid_expression) 为 glm::vec3 解析 3 个浮点数

parsing 3 floats for glm::vec3 using boost::spirit::qi (error_invalid_expression)

我可以解析一个浮点数并打印出来。 (test1, test2) 不知何故,我无法建立一个解析三个浮点数的规则。 我的最终目标是解析三个浮点数并将它们保存到 glm::vec3.

qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_]

这里是展示我到目前为止的想法的来源:

#include <iostream>
#include <string>
#include <glm\glm.hpp>
#include <boost\spirit\include\qi.hpp>
#include <boost\fusion\include\adapt_struct.hpp>

namespace qi = boost::spirit::qi;

void test1()
{
    std::string test = "1.2";
    auto it = test.begin();
    if (qi::phrase_parse(it, test.end(), qi::float_, qi::space) && (it == test.end()))
        std::cout << "test 1 ok" << std::endl;
    else
        std::cout << "test 1 not ok" << std::endl;
}

void test2()
{
    std::string test = "1.2";
    auto it = test.begin();
    float f;
    if (qi::phrase_parse(it, test.end(), qi::float_, qi::space, f) && (it == test.end()))
        std::cout << "test 2 ok " << f << std::endl;
    else
        std::cout << "test 2 not ok" << std::endl;
}

void test3()
{
    std::string test = "1.2 2.2 3.3";
    qi::rule<std::string::iterator, qi::space_type> VEC3;
    //error_invalid_expression
    VEC3 = qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_];
    auto it = test.begin();
    if (qi::phrase_parse(it, test.end(), VEC3, qi::space) && (it == test.end()))
        std::cout << "test 3 ok " << std::endl;
    else
        std::cout << "test 3 not ok" << std::endl;
}

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

void test4()
{
    std::string test = "1.2 2.2 3.3";
    qi::rule<std::string::iterator, qi::space_type> VEC3;
    //error_invalid_expression
    VEC3 = qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_];
    glm::vec3 result;
    auto it = test.begin();
    if (qi::phrase_parse(it, test.end(), VEC3, qi::space, result) && (it == test.end()))
    {
        std::cout << "test 4 ok (" << result.x << ", " << result.y << ", " << result.z << ")"<< std::endl;
    }
    else
        std::cout << "test 4 not ok" << std::endl;
}

int main(int argc, char ** argv)
{
    test1();
    test2();
    test3();
    test4();
}

test4 函数包含我尝试完成的所有内容。

编辑:

根据建议需要更改两件事:

void test4()
{
    std::string test = "1.2 2.2 3.3";
    qi::rule<std::string::iterator, glm::vec3(), qi::space_type> VEC3;
    //error_invalid_expression
    VEC3 = qi::lexeme[qi::float_ >> ' ' >> qi::float_ >> ' ' >> qi::float_];
    glm::vec3 result;
    auto it = test.begin();
    if (qi::phrase_parse(it, test.end(), VEC3, qi::space, result) && (it == test.end()))
    {
        std::cout << "test 4 ok (" << result.x << ", " << result.y << ", " << result.z << ")"<< std::endl;
    }
    else
        std::cout << "test 4 not ok" << std::endl;
}

对于简单明了的事情来说,似乎需要做大量的工作。

#include <iostream>
#include <string>
#include <sstream>

int main() {
    std::string test = "1.2 2.2 3.3";
    float f1, f2, f3;
    std::istringstream in(test);
    in >> f1 >> f2 >> f3;
    return 0;
}

sequence parser operator>>,在您的规则中用它代替 <<

VEC3 = qi::lexeme[qi::float_ >> ' ' >> qi::float_ >> ' ' >> qi::float_];

您在规则中指定了白色 space 船长,因此可以通过删除 lexeme 指令并让船长自动跳过 spaces 来进一步简化它(除非您要确保输入之间有一个 space 字符)。

VEC3 = qi::float_ >> qi::float_ >> qi::float_;

此外,如果您希望 rule 成为 return 一个值,您需要将该签名添加到规则类型

qi::rule<std::string::iterator, glm::vec3(), qi::space_type> VEC3;

这与您看到的编译错误无关,但在您的 #include 指令中使用正斜杠,它适用于所有平台。

#include <boost/spirit/include/qi.hpp>

我同意皮特·贝克尔的观点。 通常越简单越好。

现在既然要用精神了,那我们看看有什么可以更简单的:

  1. 使用c++11适配:

    BOOST_FUSION_ADAPT_STRUCT(glm::vec3, x, y, z)
    
  2. 你可以不用 adapt(演示中的simpler

  3. 如果您正在执行顶层 lexeme[] 指令,您可以(应该)从规则声明中删除船长 ¹。参见 Whosebug。com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965

  4. 更好的是,这里没有可读性规则。实际上,您会有一个包含多个规则的更大的语法。 一定不要每次解析都实例化语法(为了效率)。

演示测试平台Live On Coliru

//#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <glm/glm.hpp>
#include <iostream>
#include <string>

namespace qi = boost::spirit::qi;

glm::vec3 simpler(std::string const& test) {
    auto it = test.begin();

    glm::vec3 result;
    using namespace qi;

    if (parse(it, test.end(), 
            float_ >> ' ' >> float_ >> ' ' >> float_ >> eoi,
            result.x, result.y, result.z))
    {
        return result;
    }

    throw std::runtime_error("invalid input");
}

glm::vec3 use_skipper(std::string const& test) {
    auto it = test.begin();

    glm::vec3 result;
    using namespace qi;

    if (phrase_parse(it, test.end(), 
            float_ >> float_ >> float_ >> eoi,
            space, result.x, result.y, result.z))
    {
        return result;
    }

    throw std::runtime_error("invalid input");
}

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

glm::vec3 with_adapt(std::string const& test) {
    auto it = test.begin();

    glm::vec3 result;
    using namespace qi;

    if (phrase_parse(it, test.end(), float_ >> float_ >> float_ >>eoi, space, result))
    {
        return result;
    }

    throw std::runtime_error("invalid input");
}

int main() {
    struct { glm::vec3 (&f)(std::string const&); std::string name; } tests[] = {
        {simpler,     "simpler"},
        {use_skipper, "use_skipper"},
        {with_adapt,  "with_adapt"}
    };

    for (auto t : tests) try {
        glm::vec3 v = t.f("1.2 2.2 3.3");
        std::cout << t.name << " ok (" << v.x << ", " << v.y << ", " << v.z << ")\n";
    } catch(std::exception const& e) {
        std::cout << t.name << " fail (" << e.what() << ")\n";
    }
}

你可以轻一点Using x3。工作量并没有减少,但编译器的负担肯定减轻了。

¹ 细微差别 w.r.t。 pre/post 跳过,但如果有包含 skipper

的包含语法,这种差异就会消失