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

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

本文关键字:解析 glm vec3 3个浮 expression invalid qi spirit error 使用 boost      更新时间:2023-10-16

我可以解析一个浮点并打印它。(test1test2)不知怎的,我无法构建一个解析三个浮点值的规则。我的最终目标是解析三个浮动并将它们保存到glm::vec3中。

  • 我的规则似乎不正确:

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

  • 我不确定我是否正确使用BOOST_FUSION_ADAPT_STRUCT

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

#include <iostream>
#include <string>
#include <glmglm.hpp>
#include <boostspiritincludeqi.hpp>
#include <boostfusionincludeadapt_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;
}

我同意Pete Becker的观点<越简单越好>

现在,既然你最终要使用spirit,让我们看看什么可以更简单:

  1. 使用c++11改编:

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

  3. 如果您正在执行顶级lexeme[]指令,则可以(应该)从规则声明中删除队长。参见stackoverflow.com/questions/17072987/boost spirit captain issues/17073965#10773965

  4. 更好的是,这里没有可读性规则。事实上,你会有一个更大的语法和多个规则确保不要在每次解析时实例化语法(为了提高效率)

演示测试台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";
    }
}

使用x3,您可以稍微轻一点。这并不是说工作量减少了,但编译器的工作量肯定减轻了。

与跳过前/后的r.t.相比有细微的差异,但如果有一个包含语法的队长

,这种差异就会消失

序列解析器操作符是>>,在您的规则中使用它而不是<<

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

您已经在规则中指定了一个空格队长,因此可以通过删除lexeme指令并让队长自动跳过空格来进一步简化它(除非您希望确保输入之间只有一个空格字符)。

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

此外,如果您希望规则返回值,则需要将该签名添加到规则类型中

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

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

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

对于一些简单的东西来说,似乎需要做大量的工作。

#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;
}