Kevin
Kevin

Reputation: 18243

C++ boost::spirit::karma rule for variants

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!

Upvotes: 3

Views: 569

Answers (1)

sehe
sehe

Reputation: 393114

1. Simplicity Rules

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

2. A more involved sample

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.)

Upvotes: 3

Related Questions