I have a boost::variant
in my program that takes types of double
, uint16_t
, std::string
, etc. I'm storing these and I'd like to use boost::karma
to generate/print them out. I'm new to boost::spirit
, but I understand it works well with variants. What options do I have to go about this? What would a simple grammar/rule look like to generate one of these? Any help would be great!
The absolute simplest example I can think of, showcases how karma can even synthesize an auto_
rule for your particular variant on the fly[1] :
#include <boost/spirit/include/karma.hpp>
namespace karma = boost::spirit::karma;
int main() {
typedef boost::variant<double, unsigned int, std::string> V;
for(auto v : { V{42u}, V{3.1416}, V{"Life Of Pi"} })
std::cout << karma::format(karma::auto_, v) << "\n";
}
Prints:
42
3.142
Life Of Pi
Easy as pie!
The equivalent in a separate grammar: Live On Coliru
A more involved grammar (also Live On Coliru), which shows you how Spirit's Attribute compatibility rules DoTheRightThing™ magically:
#include <boost/spirit/include/karma.hpp>
namespace karma = boost::spirit::karma;
typedef boost::variant<double, unsigned int, std::string> V;
struct gen : karma::grammar<boost::spirit::ostream_iterator, V()> {
gen() : gen::base_type(start)
{
using namespace karma;
start = my_real | my_uint | my_text;
my_uint = "The value is unsigned integral value (" << uint_ << ")";
my_real = "The value is double precision floating point value (" << double_ << ")";
my_text = "The value is an epos: '" << *quoted_char << "'";
quoted_char = '\\' << char_("'") | graph | "\\x" << uint_generator<uint8_t, 16>();
}
private:
karma::rule<boost::spirit::ostream_iterator, V()> start;
karma::rule<boost::spirit::ostream_iterator, double()> my_real;
karma::rule<boost::spirit::ostream_iterator, unsigned int()> my_uint;
karma::rule<boost::spirit::ostream_iterator, std::string()> my_text;
karma::rule<boost::spirit::ostream_iterator, uint8_t()> quoted_char;
};
int main()
{
for(auto v : { V{42u}, V{3.1416}, V{"It's a beautiful day!"} })
std::cout << karma::format(gen(), v) << "\n";
}
This prints:
The value is unsigned integral value (42)
The value is double precision floating point value (3.142)
The value is an epos: 'It\'s\x20a\x20beautiful\x20day!'
main
could also be written as
int main() {
std::cout << karma::format(gen() % "\n", std::vector<V>{42u,3.1416,"It's a beautiful day!"}) << "\n";
}
Which should give you a glimpse of just how versatile the Spirit Parser/Generator framework can be.
[1] As long as the auto parser generator trait is present; Spirit provides them for a host of types, including uint
, std::string
, double
and variant
(but also optionals, vectors, maps, anything that can be adapted as a Fusion sequence etc. etc.)