Search code examples
boostboost-spirit-karma

boost karma semantic action call map


I need to map values to a std::string ( with the following map, and BOOST_FUSION_ADAPT_STRUCT )

    std::map< TYPEX, std::string> author2name;
    struct Emp
   {
     std::string name;
     TYPEX author;
   };

With the following code i want to generate my output:

karma::rule< it, std::string()> quote = '"' >> karma::string >> '"';
karma::rule< it, Emp> emp = karma::delimit('\t')[ quite << quite[ author2name[ karma::_1] ]];

Emp x;
karma::generate( std::ostream_iterator<char>(std::cout), emp, x);

But it doesn't compile.

And is there a way that i could write a header like this:

karma::rule< it, std::vector<std::string>()> header = karma::delimit('\t')[ % quote];
karma::rule< it, Emp> emp = header >> karma::eol >> karma::delimit('\t')[ quite << quite[ author2name[ karma::_1] ]];

karma::generate( std::ostream_iterator<char>(std::cout), {"A", "B", "C"},emp, x);

Solution

  • There is a number of small paper-cuts that killed you there :)

    Working example:

    Live On Coliru

    #include <boost/fusion/adapted.hpp>
    #include <boost/spirit/include/karma.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <map>
    
    namespace karma = boost::spirit::karma;
    namespace phx   = boost::phoenix;
    
    enum TYPEX { AUTHOR1, AUTHOR2, AUTHOR3, AUTHOR4 };
    
    std::map<TYPEX, std::string> author2name;
    struct Emp {
        std::string name;
        TYPEX author;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(Emp, name, author) // boost 1_59
    //BOOST_FUSION_ADAPT_STRUCT(Emp, (std::string, name)(std::string, author)) // older boost
    
    int main() {
        using it = boost::spirit::ostream_iterator;
    
        karma::rule<it, std::string()> quote;
        karma::rule<it, TYPEX()> author;
        karma::rule<it, Emp()> emp;
    
        {
            using namespace karma;
            quote  %= '"' << string << '"';
            author  = quote [ _1 = phx::ref(author2name)[ _val ] ];
            emp    %= delimit('\t')[ quote << author ];
        }
    
        Emp x { "one", AUTHOR2 };
        author2name[AUTHOR2] = "TWO!";
        std::cout << karma::format(emp, x);
    }
    

    Prints:

    "one"   "TWO!"  
    

    The things that caused trouble:

    • suggest to use boost::spirit::ostream_iterator and karma::format for more user-friendly API
    • Add the missing parentheses on emp:

      karma::rule<it, Emp()> emp;
      

      NOTE: very recent boost (1_59 IIRC) doesn't not require these anymore. Which is why I found out only on Coliru

    • Here:

      quote[ author2name[ karma::_1] ]
      

      you index [] into a std::map using ... qi::_1. That can't compile. What you wanted was to invoke the Phoenix lazy expression template of operator[]. You have to include the Phoenix header and force author2name to be a Phoenix reference actor:

      quote [ _1 = phx::ref(author2name)[_1] ]
      

      Note also, assigning back to _1 is important!

    • Also, to have an auto-rule in the presence of Semantic Actions, you need to assign the rule using %= (otherwise Karma will suppress all automatic attribute propagation)