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.
Your problem can be divided in two:
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.
#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;
}