1

I honestly give up (like many other before me) to find by myself the syntax of this still pretty simple generator of the boost-spirit-karma Library. I would like to display, before the string, as many white spaces as characters in the string:

typedef enum {A, B, C} E;

class EName : public ka::symbols<E, std::string>
{
public:
    EName() {add (A,"A") (B,"the B") (C,"a C");}
};

class grm: public ka::grammar<iterator, E()>
{
public:
    grm():grm::base_type(start)
    {
        namespace phx = boost::phoenix;
        namespace ka = boost::spirit::karma;        
        start = ka::duplicate[ka::repeat(phx::bind(&std::string::size,b))[ka::lit(' ')] << b];
    }
private:

    ka::rule<iterator,E()> start;
    EName b;
};

int main(int argc, char * argv[])
{
    grm g;
    E e = A;

    std::string generated;
    std::back_insert_iterator<std::string> sink(generated);
    ka::generate(sink,g,e);
    std::cout << generated << "\n";
    generated.clear();
    e = B;
    ka::generate(sink,g,e);
    std::cout << generated << "\n";
    return 0;
}

The expected output is thus one white space followed by "A" and on the next line 5 white spaces followed by "the B" (as "the B" is a 5 characters string).

I understand that maybe the variable "b" is not accessible in the context of the argument of the ka::repeat()[] generator...I tried ka::_val instead without success. I actually don't have enough experience with both karma, phoenix and fusion to build a path to the answer, although I probably have access to all needed information in the documentation. Hence I would also appreciate a few hint on how I could come to the answer just by the documentation (or by deduction) rather than by experience.

UPDATE:

I tried using a attribute cast without success:

namespace boost {
    namespace spirit {
        namespace traits {
            template <>
            struct transform_attribute<const E, std::string, ka::domain>
            {
                typedef std::string type;
                static type pre(const E & e) {
                    EName s;
                    int num = s.find(e)->size();
                    return std::string(num, ' ');
                }
            };
} } }

followed by:

start = ka::attr_cast<std::string>(ka::string) << b;

But neither does it compile.

Heyji
  • 1,113
  • 8
  • 26

2 Answers2

1

I was not that far, so I post here my first working attempt. Other solutions are welcome as well.

namespace ka = boost::spirit::karma;    

typedef enum {A, B, C} E;

class EName : public ka::symbols<E, std::string>
{
public:
    EName() {add (A,"A") (B,"the B") (C,"a C");}
};

namespace boost {
    namespace spirit {
        namespace traits {
            template <>
            struct transform_attribute<const E, std::string, ka::domain>
            {
                typedef std::string type;
                static type pre(const E & e) {
                    EName s;
                    int num = s.find(e)->size();
                    return std::string(num, ' ');
                }
            };
} } }

class grm: public ka::grammar<iterator, E()>
{
public:
    grm():grm::base_type(start)
    {    
        start = ka::duplicate[ka::attr_cast<std::string>(ka::string) << b];
    }
private:

    ka::rule<iterator,E()> start;
    EName b;
};

int main(int argc, char * argv[])
{
    grm g;
    E e = A;

    std::string generated;
    std::back_insert_iterator<std::string> sink(generated);
    ka::generate(sink,g,e);
    std::cout << generated << "\n";
    generated.clear();
    e = B;
    ka::generate(sink,g,e);
    std::cout << generated << "\n";
    return 0;
}
Heyji
  • 1,113
  • 8
  • 26
1

Your problem can be divided in two:

  • Get a string from the enum value.
  • Prepend as many spaces as characters there are in that string.

The second part of the problem is quite straightforward using the right_align directive. You can simply use:

prepend_spaces = ka::right_align(2*phx::size(ka::_val))[ka::string]; 

(If you wanted to use something else besides simply whitespace you could use the second parameter to right_align (for example ka::right_align(2*phx::size(ka::_val),ka::lit('*'))[ka::string]))

For the first part, you could do something with attr_cast as you have shown. In the code below I have used phx::bind in order to obtain the string from the symbol table.

Running on WandBox

#include <iostream>
#include <string>
#include <vector>

#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace phx = boost::phoenix;
namespace ka = boost::spirit::karma;  

typedef enum {A, B, C} E;

class EName : public ka::symbols<E, std::string>
{
public:
    EName() {add (A,"A") (B,"the B") (C,"a C");}
};

template <typename Iterator>
class grm: public ka::grammar<Iterator, E()>
{
public:
    grm():grm::base_type(start)
    {

        prepend_spaces = ka::right_align(2*phx::size(ka::_val))[ka::string]; 
        start = prepend_spaces[ka::_1=phx::bind(&EName::at<E>,phx::ref(name),ka::_val)]; 
    }
private:

    ka::rule<Iterator,E()> start;
    ka::rule<Iterator,std::string()> prepend_spaces;
    EName name;
};

int main(int argc, char * argv[])
{
    grm<std::back_insert_iterator<std::string> > g;
    E e = A;

    std::string generated;
    std::back_insert_iterator<std::string> sink(generated);
    ka::generate(sink,g,e);
    std::cout << generated << "\n";
    generated.clear();
    e = B;
    ka::generate(sink,g,e);
    std::cout << generated << "\n";

    generated.clear();
    std::vector<E> v {A,B,C};
    ka::generate(sink,+g,v);
    std::cout << generated << "\n";
    return 0;
}
llonesmiz
  • 155
  • 2
  • 11
  • 20
  • Nice. There should be a shorter path with something like ka::_1 = phx::bind(&EName::at, phx::ref(name),ka::_val) but I cannot manage to get it working. – Heyji Jul 27 '16 at 07:27
  • [This](http://melpon.org/wandbox/permlink/l8U5r0fasq4fZ08c) was what I meant with an approach using "attr_cast" (`transform_attribute` actually, since `attr_cast` does not seem to be required). I didn't like this approach because I have seen lots of (maybe already solved in the current version) problems with `attr_cast`, specially with Karma. – llonesmiz Jul 27 '16 at 16:10