Search code examples
boost-spirit-karma

Using variant storing container in karma


boost::variant<boost::container::vector<int>, std::string> tmp = "test";
std::string use;
namespace karma = boost::spirit::karma;
bool r = karma::generate(std::back_insert_iterator<std::string>(use), +karma::int_ |   *karma::char_, tmp);

The result is r = false, use = "". However, I expect use = "test".

There is another example

boost::variant<std::vector<int>, std::string> tmp = "test";
std::string use;
namespace karma = boost::spirit::karma;
bool r = karma::generate(std::back_insert_iterator<std::string>(use), +karma::int_ |  +karma::char_, tmp);

The result is still r = false, use = "". However. What' wrong?


Solution

  • There is a big difference between how the alternative parser and alternative generator work. The alternative parser tries to match its operands one by one until one of them succeeds. In contrast the alternative generator uses the operand that matches exactly the attribute you want to generate and if there is none that matches it does nothing. If there are several that do match, those are tried one by one until one of them succeeds. The easiest way to solve your problem is creating karma::rules that expose the exact attributes in your variant.

    Example on Coliru

    #include <iostream>
    #include <vector>
    #include <boost/spirit/include/karma.hpp>
    #include <boost/container/vector.hpp>
    
    int main()
    {
        boost::variant<boost::container::vector<int>, std::string> tmp = "test";
        std::string use;
        namespace karma = boost::spirit::karma;
        //Your original code
        bool r = karma::generate(std::back_insert_iterator<std::string>(use), +karma::int_ |   *karma::char_, tmp);
        std::cout << "r=" << std::boolalpha << r << ", Use: \""<< use << '"' << std::endl;
    
        //Create rules to expose explicitly the attributes you want
        use.clear();
        karma::rule<std::back_insert_iterator<std::string>,boost::container::vector<int>()> ints_rule = karma::int_%',';
        karma::rule<std::back_insert_iterator<std::string>,std::string()> string_rule = *karma::char_;
        r = karma::generate(std::back_insert_iterator<std::string>(use), ints_rule | string_rule, tmp);
        std::cout << "r=" << std::boolalpha << r << ", Use: \""<< use << '"' << std::endl;
    
        //Same test using ints
        use.clear();
        boost::container::vector<int> tmp_vector;
        tmp_vector.push_back(1);
        tmp_vector.push_back(2);
        tmp=tmp_vector;
        r = karma::generate(std::back_insert_iterator<std::string>(use), ints_rule | string_rule, tmp);
        std::cout << "r=" << std::boolalpha << r << ", Use: \""<< use << '"' << std::endl;
    
        //An example in which the variant has the exact same attributes exposed by the alternative generator
        use.clear();
        boost::variant<std::vector<int>, std::vector<char> > tmp2;
        std::vector<char> string;
        string.push_back('t');
        string.push_back('e');
        string.push_back('s');
        string.push_back('t');
        tmp2=string;
        r = karma::generate(std::back_insert_iterator<std::string>(use), karma::int_%',' | *karma::char_,tmp2);
        std::cout << "r=" << std::boolalpha << r << ", Use: \""<< use << '"' << std::endl;
    }