Search code examples
boostboost-spiritboost-spirit-karma

Passing delimiter to a child rule in boost spirit


This is in continuation of my question mentioned here

Inconsistent Generator directive column behavior in boost karma

I want to wrap that rule into another rule and when doing so , pass the column directive to the child rule but I cannot figure out what's the right way of doing that.

Here is the code below

#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/struct.hpp>
#include <boost/fusion/include/nview.hpp>

///////////////////////////////////////////////////////////////////////////////
namespace client
{


struct BoxData
{
    int num;
    std::string datatype;
    std::string dataname;
    std::string inputicon;
};

struct Box
{
    std::vector<BoxData> box_data;
};

typedef std::back_insert_iterator<std::string> iterator_type;
}

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

BOOST_FUSION_ADAPT_STRUCT(
        client::Box,
        (std::vector<client::BoxData>, box_data)
)


///////////////////////////////////////////////////////////////////////////////
int main()
{
    // some employees
    std::vector<client::BoxData> 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"},
    };

    client::Box box;
    ///TODO prevent copying of the vector
    box.box_data = employees;

    // now print them all
    std::string generated;
    {
        using namespace boost::spirit::karma;

        using Sink = client::iterator_type;
        using BoxDataAttr = client::BoxData;
        using BoxAttr = client::Box;

        rule<Sink, BoxDataAttr(),space_type> small_box = "<B>" << string << "<1>" << string << "<2>" << string << "<3>";

        rule<Sink , BoxAttr()> big_box = "<Start>" << *small_box << "<End>" ;

        generate(Sink(generated),[big_box],box);
    }

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

In the line

rule<Sink , BoxAttr()> big_box = "<Start>" << *small_box << "<End>" ;

I want to pass the

columns(2, "delimiter\n")[small_box]

delimiter so that the column delimiter is applied to the small_box rule.

So the expected output becomes

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

Eventually since there would be vector which would pass another custom delimiter to the big_box rule.

Background

I am planning to write a diagnostic interface as an exercise for learning various C++ concepts which involves the following.

1.Parsing data from an IDL file .I haven't defined the exact format yet but will look something similar to this

struct Data1
{
int command_one output "fa fa-wrench";
float commmand_two output "fa fa-sensor";
}

struct Data2
{
bool a input switch;
int b input progress_bar {0,100} ;
}
  1. Generating HTML elements from the IDL file. Each struct is wrapped in a box which contains the individual elements inside them in smaller boxes with their icons/optional parts. The nth delimiter is useful for generating the bootstrap grid for placement of these boxes on the screen.

  2. Complete the HTML page with a websocket implementation that updates DOM elements automatically when JSON data is received containing DOM element name -value pair . Similarly for each input , a send command is implemented to wrap it in a JSON and send to the web server.

  3. Auto generate the code for the webserver so that the only code left to implement is receiving/transmitting data from an external source with all conversions to/from JSON generated.

The following use case prompted me to look for a parser/generator solution.


Solution

  • I'm not too sure about the cause for your question, but it seems to me the solution from yesterday holds:

    Live On Coliru

        rule<Sink, BoxDataAttr()> small_box = "<B>" << string << "<1>" << string << "<2>" << string << "<3>";
    
        rule<Sink, BoxAttr()> big_box = "<Start>" << columns(4, "delimiter\n") [small_box % eol] << "<End>" ;
    
        generate(Sink(generated), big_box, employees);
    

    Prints

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

    On a hunch, you might have missed the fact that you had , space_type as the delimiter on the small_box rule.

    Eventually since there would be vector which would pass another custom delimiter to the big_box rule.

    At this point I question your choice of tools. Why not roll your own pretty-printing here. I expect it to be considerably simpler. Also read up on https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem

    Bonus:

    Here's avoiding copying the vector:

    client::Box 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"},
    } };
    

    More generally, move semantics could have served:

    box.box_data = std::move(employees);