Reputation: 35
I'm trying to create a GPIB parser using Spirit.Qi. Sometimes the response can
be either an error or a normal response. This seemed like a good use case for
the alternative parser which yields a boost::variant
; however, if one of the
variant types contains a string the code fails to compile. Here is a simplified
version that reproduces the error.
struct C1 {
std::string h{""};
int i{0};
};
BOOST_FUSION_ADAPT_STRUCT(C1, (std::string, h)(int, i))
struct C2 {
std::string h{""};
std::string c{};
};
BOOST_FUSION_ADAPT_STRUCT(C2, (std::string, h)(std::string, c))
using VariantType = boost::variant<C1, C2>;
int main() {
std::string s2{"C2:Zoo3"};
VariantType v1;
if(qi::parse(s1.begin(), s1.end(),
(qi::string("C1") >> ":" >> qi::int_) |
(qi::string("C2") >> ":" >> *qi::char_),
v1)) {
if(boost::get<C1>(&v1)) {
auto a1 = boost::get<C1>(v1);
std::cout << "Parsing Succeeded, a1 = " << a1.h << ":"
<< a1.i << std::endl;
}
else {
auto a2 = boost::get<C2>(v1);
std::cout << "Parsing Succeeded, a2 = " << a2.h << ":"
<< a2.c << std::endl;
}
}
else {
std::cout << "Parsing Failed" << std::endl;
}
return 0;
}
I've experimented with various parsers other than *qi::char_
(eg. qi::string
) to no avail. If I change C2::c
to a char
it works ok for 1 char. The best workaround I have found so far is to change C2::c
to a std::vector<char>
, this works fine but isn't ideal. I also tried telling Qi that std::string
is a container like here. But qi knows what a std::string
is so I'm pretty sure it ignores my customization. I think this is all because std::string
isn't a POD and wasn't supported in unions until the rules were relaxed, but it works with boost::variant
when it isn't in a struct, and std::vector
works. Any ideas/workarounds would be appreciated.
*Note: I didn't post the compiler errors as they are long and mangled and I figured this was a known issue of std::string
and variants. If they would be helpful let me know and I'll post them.
Upvotes: 2
Views: 409
Reputation: 392883
There's nothing wrong with the code, as far as I can tell.
I've tested on c++1{1,4,y} and boost 1.{57,58}.0, using gcc {4.9,5.x} and clang++ 3.5.
I suspect you may have an awkward Boost verion. Try using qi::as_string[*qi::char_]
there. ¹
#include <iostream>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
struct C1 {
std::string h{""};
int i{0};
friend std::ostream& operator<<(std::ostream& os, C1 const& c1) {
return os << "C1 {h:'" << c1.h << "', i:'" << c1.i << "'}";
}
};
struct C2 {
std::string h{""};
std::string c{};
friend std::ostream& operator<<(std::ostream& os, C2 const& c2) {
return os << "C2 {h:'" << c2.h << "', c:'" << c2.c << "'}";
}
};
BOOST_FUSION_ADAPT_STRUCT(C1, (std::string, h)(int, i))
BOOST_FUSION_ADAPT_STRUCT(C2, (std::string, h)(std::string, c))
using VariantType = boost::variant<C1, C2>;
namespace qi = boost::spirit::qi;
int main() {
VariantType value;
for(std::string s1 : {
"C2:Zoo3",
"C1:1234"
})
{
if(qi::parse(s1.begin(), s1.end(),
(qi::string("C1") >> ":" >> qi::int_) |
(qi::string("C2") >> ":" >> *qi::char_),
value))
std::cout << "Parsing Succeeded: " << value << "\n";
else
std::cout << "Parsing Failed" << std::endl;
}
}
Prints
Parsing Succeeded: C2 {h:'C2', c:'Zoo3'}
Parsing Succeeded: C1 {h:'C1', i:'1234'}
¹ I don't recommend qi::attr_cast<>
as I remember this having had an awkward bug in awkwardly old boost versions.
Upvotes: 1