【问题标题】:parsing 3 floats for glm::vec3 using boost::spirit::qi (error_invalid_expression)使用 boost::spirit::qi (error_invalid_expression) 为 glm::vec3 解析 3 个浮点数
【发布时间】:2016-01-07 18:43:58
【问题描述】:

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

  • 我的规则似乎不正确:

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

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

这是一个显示我到目前为止的想法的来源:

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

【问题讨论】:

    标签: c++ boost boost-spirit-qi


    【解决方案1】:

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

    现在,既然您最终将使用 Spirit,让我们看看可以更简单的方法:

    1. 使用 c++11 适配:

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

    3. 如果您正在执行顶级 lexeme[] 指令,您可以(应该)从规则声明中删除船长¹。请参阅 stackoverflow.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. 的细微差别前/后跳过,但如果有一个包含跳过的语法,那么这种差异就会消失

    【讨论】:

      【解决方案2】:

      sequence parser operator 是 &gt;&gt;,在您的规则中使用它而不是 &lt;&lt;

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

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

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

      另外,如果您希望 rule 返回一个值,您需要将该签名添加到规则类型

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

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

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

      【讨论】:

      • 很好...我觉得有点笨,但谢谢:)。您还可以帮助解决问题的“BOOST_FUSION_ADAPT_STRUCT”部分吗? test4 的结果没有保存在我的电脑上。
      • @Johannes 啊是的,错过了,当希望规则合成返回值时,需要添加到规则类型中。
      【解决方案3】:

      对于一些简单的事情来说似乎需要做很多工作。

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

      【讨论】:

      • 整个事情将在某一时刻包括所有的波前对象和波前材料文件。所以它不会像那样简单 - 但你在一定程度上是正确的。
      • @Johannes - 我想可能不止这些。不过,更简单通常更好。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-26
      • 1970-01-01
      • 2012-12-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-10
      相关资源
      最近更新 更多