Search code examples
c++boostboost-spirit-qi

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


I can parse one float and print it. (test1, test2) Somehow I am unable to build a rule that parses three floats. My final goal is to parse three floats and save them to a glm::vec3.

  • My rule seems to be incorrect:

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

  • I am not sure if i am using BOOST_FUSION_ADAPT_STRUCT correctly

Here is a source to show what i came up with so far:

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

The test4 function contains everything I try to get done.

EDIT:

As suggested two things need to be changed:

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

Solution

  • The sequence parser operator is >>, use that instead of << in your rules

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

    You've specified a whitespace skipper in your rule, so it can be further simplified by removing the lexeme directive and letting the skipper skip spaces automatically (unless you want to ensure there is a single space character in between the inputs).

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

    Also, if you want your rule to return a value, you need to add that signature to the rule type

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

    This is unrelated to the compilation error you're seeing, but use forward slashes in your #include directives, that works on all platforms.

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