boostboost-spiritboost-spirit-karma

Inconsistent Generator directive column behavior in boost karma


I am writing a karma generator to generate a HTML page and I am experiencing inconsistent behavior while using column directive. It could very well be my understanding of how it works.

Basically I am generating a grid which requires me to insert some delimiters after every 2 occurrences of the data.

The below is a basic program I adopted to do a test run.

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/struct.hpp>
#include <boost/fusion/include/nview.hpp>
#include <boost/assign/std/vector.hpp>

namespace fusion = boost::fusion;
namespace karma = boost::spirit::karma;

///////////////////////////////////////////////////////////////////////////////
namespace client
{
    //  Our employee struct
    struct employee
    {
        int num;
        std::string datatype;
        std::string dataname;
        std::string inputicon;
    };

    // define iterator type
    typedef std::back_insert_iterator<std::string> iterator_type;

}

BOOST_FUSION_ADAPT_STRUCT(
        client::employee,
        (int, num)
                (std::string, datatype)
                (std::string, dataname)
                (std::string, inputicon)
)

///////////////////////////////////////////////////////////////////////////////
int main()
{
    std::string str;

    // some employees
    client::employee john = { 25, "int", "sra_command","fa fa-wrench" };
    client::employee mary = { 25, "float", "swt_command","fa fa-wrench" };
    client::employee tom = { 25, "double", "msc_command","fa fa-mobile" };

    // now make a list of all employees and print them all
    std::vector<client::employee> employees;
    {
        using namespace boost::assign;
        employees += john, mary, tom;
    }

    {
        typedef
        fusion::result_of::as_nview<client::employee const, 1, 2, 3>::type
                names_and_salary;


        karma::rule<client::iterator_type, names_and_salary()> small_box  =
                                                   "<startofblock>"  <<  karma::string << "<after_first>"
                                                                    << karma::string << "<after_second>"
                                                                    << karma::string << "<after_third>";

        std::string generated;
        typedef std::back_insert_iterator<std::string> sink_type;
        sink_type sink(generated);
        karma::generate_delimited(sink, karma::columns(2,karma::string("nth_delimiter"))[small_box % karma::eol],karma::space,employees );

        std::cout << generated << std::endl;
    }
    return 0;
}

The above generates the following output :

<startofblock>int<after_first>sra_command<after_second>fa fa-wrench<after_third> 
 nth_delimiter<startofblock>float<after_first>swt_command<after_second>fa fa-wrench<after_third> 
 nth_delimiter<startofblock>double<after_first>msc_command<after_second>fa fa-mobile<after_third> nth_delimiter

As noted , the nth delimiter occurs after every generation instead of every second.

The expected output is

<startofblock>int<after_first>sra_command<after_second>fa fa-wrench<after_third> 
<startofblock>float<after_first>swt_command<after_second>fa fa-wrench<after_third>  
 nth_delimiter<startofblock>double<after_first>msc_command<after_second>fa fa-mobile<after_third> 

Solution

  • I think this comes close to what you want to achieve. Like I said, the last (incomplete) run will still be "terminated" with an nth_delimiter tag:

    Live On Coliru

    #include <boost/spirit/include/karma.hpp>
    #include <boost/fusion/include/struct.hpp>
    #include <boost/fusion/include/nview.hpp>
    
    ///////////////////////////////////////////////////////////////////////////////
    namespace client {
        struct employee
        {
            int num;
            std::string datatype;
            std::string dataname;
            std::string inputicon;
        };
    
        typedef std::back_insert_iterator<std::string> iterator_type;
    }
    
    BOOST_FUSION_ADAPT_STRUCT(client::employee, /*num,*/ datatype, dataname, inputicon)
    
    ///////////////////////////////////////////////////////////////////////////////
    int main() {
        // some employees
        std::vector<client::employee> const employees {
            { 25, "int",    "sra_command","fa fa-wrench" },
            { 26, "float",  "swt_command","fa fa-wrench" },
            { 27, "double", "msc_command","fa fa-mobile" },
            { 28, "int",    "sra_command","fa fa-wrench" },
            { 29, "float",  "swt_command","fa fa-wrench" },
            { 30, "double", "msc_command","fa fa-mobile" },
            { 31, "int",    "sra_command","fa fa-wrench" },
            { 32, "float",  "swt_command","fa fa-wrench" },
            { 33, "double", "msc_command","fa fa-mobile" },
        };
    
        // now print them all
        std::string generated;
        {
            using namespace boost::spirit::karma;
    
            using Sink = client::iterator_type;
            //using Attr = boost::fusion::result_of::as_nview<client::employee const, 1, 2, 3>::type;
            using Attr = client::employee;
            rule<Sink, Attr()> small_box  = "<B>" << string << "<1>" << string << "<2>" << string << "<3>";
    
            generate(Sink(generated), columns(2, "<nth_delimiter>\n") [+small_box], employees);
        }
    
        std::cout << generated << std::endl;
    }
    

    Prints

    <B>int<1>sra_command<2>fa fa-wrench<3><B>float<1>swt_command<2>fa fa-wrench<3><nth_delimiter>
    <B>double<1>msc_command<2>fa fa-mobile<3><B>int<1>sra_command<2>fa fa-wrench<3><nth_delimiter>
    <B>float<1>swt_command<2>fa fa-wrench<3><B>double<1>msc_command<2>fa fa-mobile<3><nth_delimiter>
    <B>int<1>sra_command<2>fa fa-wrench<3><B>float<1>swt_command<2>fa fa-wrench<3><nth_delimiter>
    <B>double<1>msc_command<2>fa fa-mobile<3><nth_delimiter>
    

    Bonus:

    The problem with smallbox % eol is that smallbox is an element, and eol too:

        generate(Sink(generated), columns(4, "<nth_delimiter>") [small_box % eol], employees);
    

    Live On Coliru

    <B>int<1>sra_command<2>fa fa-wrench<3>
    <B>float<1>swt_command<2>fa fa-wrench<3>
    <nth_delimiter><B>double<1>msc_command<2>fa fa-mobile<3>
    <B>int<1>sra_command<2>fa fa-wrench<3>
    <nth_delimiter><B>float<1>swt_command<2>fa fa-wrench<3>
    <B>double<1>msc_command<2>fa fa-mobile<3>
    <nth_delimiter><B>int<1>sra_command<2>fa fa-wrench<3>
    <B>float<1>swt_command<2>fa fa-wrench<3>
    <nth_delimiter><B>double<1>msc_command<2>fa fa-mobile<3><nth_delimiter>