3

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!

Kevin
  • 16,549
  • 8
  • 60
  • 74

1 Answers1

3

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

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Added some nice samples. I'm pretty sure this should help you on your way :) **[Live On Coliru](http://coliru.stacked-crooked.com/a/ebe63219dd283ec0)** – sehe Oct 10 '14 at 22:49
  • thanks so much for this insightful response! this helped me tremendously! – Kevin Oct 13 '14 at 20:55