Reputation: 631
I'm new to Spirit::Qi and I'm trying to write a simple Wavefront Obj parser. I've followed the tutorials from the Boost::Spirit documentation site (link) and I got most of the inline rules working. I've started experimenting with grammars, but I cannot seem to get them working. After a while I did get it to compile, but the parsing fails. I really don't know what I am doing wrong.
To start out simple, I've created a simple text file containing the following:
v -1.5701 33.8087 0.3592
v -24.0119 0.0050 21.7439
v 20.8717 0.0050 21.7439
v 20.8717 0.0050 -21.0255
v -24.0119 0.0050 -21.0255
v -1.5701 0.0050 0.3592
Just to be sure: Reading the input file works fine.
I've written a small function that should parse the input string, but for some reason it fails:
bool Model::parseObj( std::string &data, std::vector<float> &v )
{
struct objGram : qi::grammar<std::string::const_iterator, float()>
{
objGram() : objGram::base_type(vertex)
{
vertex = 'v' >> qi::float_
>> qi::float_
>> qi::float_;
}
qi::rule<std::string::const_iterator, float()> vertex;
};
objGram grammar;
return qi::phrase_parse( data.cbegin(), data.cend(),
grammar, iso8859::space, v );
}
qi::phrase_parse keeps returning false and the std::vector v is still empty at the end...
Any suggestions?
EDIT:
After adding adding space skippers (is that the correct name?), only the first 'v' is added to the std::vector encoded as a float (118.0f), but the actual numbers aren't added. My guess is that my rule isn't correct. I want to only add the numbers and skip the v's.
Here is my modified function:
bool Model::parseObj( std::string &data, std::vector<float> &v )
{
struct objGram : qi::grammar<std::string::const_iterator, float(), iso8859::space_type>
{
objGram() : objGram::base_type(vertex)
{
vertex = qi::char_('v') >> qi::float_
>> qi::float_
>> qi::float_;
}
qi::rule<std::string::const_iterator, float(), iso8859::space_type> vertex;
} objGrammar;
return qi::phrase_parse( data.cbegin(), data.cend(),
objGrammar, iso8859::space, v );
}
Upvotes: 2
Views: 573
Reputation: 393709
Your rule declares the wrong exposed attribute. Change it:
qi::rule<std::string::const_iterator, std::vector<float>(), iso8859::space_type> vertex;
However, since you don't template your grammar struct on anything (like iterator/skipper type), it makes no sense to have a grammar struct. Instead, let phrase_parse
simply deduce the iterator, skipper and rule types all at once and write:
bool parseObj(std::string const& data, std::vector<float> &v )
{
return qi::phrase_parse(
data.cbegin(), data.cend(),
'v' >> qi::float_ >> qi::float_ >> qi::float_,
qi::space, v);
}
I think you'll agree that's more to the point. And as a bonus, it "just works"(TM) because of the awesomeness that is automatic attribute propagation rules.
However, seeing your grammar, you'll certainly want to see these:
How to parse space-separated floats in C++ quickly? showing how to parse into a vector of structs
struct float3 {
float x,y,z;
};
typedef std::vector<float3> data_t;
with little or no extra work. Oh and it benchmarks the spirit approach reading a 500Mb file against the competing fscanf
and atod
calls. So, it parses multiple lines at once :)
Use the qi::double_
parser instead of qi::float_
even if you're ultimately assigning to single-precision float
variables. See Boost spirit floating number parser precision
Upvotes: 2