2

Hi what i want is to generate some text according to what i pass into the generator for example

struct C1
{
    int Getter()
    {
        return 3;
    }
};

struct C2
{
    int Getter()
    {
        return 5;
    }
};




template<typename Iterator>
struct Temp:
    public karma::grammar<Iterator,boost::variant<C1*,C2*>()>
{
    Temp():
        Temp::base_type(start1)
    {
        using karma::int_;
        using karma::float_;
        using karma::lit;

        start1 = c1 | c2;
        c1 = karma::lazy(boost::phoenix::bind(&C1::Getter,karma::_1));
        c2 = karma::lazy(boost::phoenix::bind(&C2::Getter,karma::_1));

    }

    karma::rule<Iterator,boost::variant<C1*,C2*>()> start1;
    karma::rule<Iterator,C1*() > c1;
    karma::rule<Iterator,C2*() > c2;

... and then invoke something like

std::string str;
std::back_insert_iterator<std::string> out(str);
Temp<std::back_insert_iterator<std::string> > bla;
C1 c1;
karma::generate(out, bla,&c1);

I'm not even sure if i am using the right generator but it says that lazy is able to transform from what is in the attributes into something that returns the functor inside

sehe
  • 374,641
  • 47
  • 450
  • 633
Martin Kosicky
  • 471
  • 4
  • 12
  • [karma::lazy](http://www.boost.org/libs/spirit/doc/html/spirit/karma/reference/auxiliary/lazy.html) requires that its argument evaluate to a generator object, so what you are trying won't work. You simply need to use [semantic actions](http://liveworkspace.org/code/DOrJ9%240). –  Feb 22 '13 at 14:56
  • 1
    I greatly expanded on my answer showcasing two approaches based on `BOOST_FUSION_ADAPT_ADT` – sehe Feb 22 '13 at 16:52

1 Answers1

4

The simplest fix would be

c1 = int_ [ karma::_1 = boost::phoenix::bind(&C1::Getter,karma::_val) ];
c2 = int_ [ karma::_1 = boost::phoenix::bind(&C2::Getter,karma::_val) ];

BOOST_FUSION_ADAPT_ADT

I think you'd like to learn about BOOST_FUSION_ADAPT_ADT() too:

struct C1 { int Getter() const { return 3; } void Setter(int){} }; 
struct C2 { int Getter() const { return 5; } void Setter(int){} }; 

BOOST_FUSION_ADAPT_ADT(C1, (int,int,obj.Getter(),obj.Setter(val)));
BOOST_FUSION_ADAPT_ADT(C2, (int,int,obj.Getter(),obj.Setter(val)));

Alternative 1: attr_cast

Use attr_cast and pass by value. Here's a sample without a grammar for brevity:

using namespace karma;
std::cout << karma::format("C1:" << attr_cast<C1>(int_) | "C2:" << attr_cast<C2>(int_), c1) << "\n";
std::cout << karma::format("C1:" << attr_cast<C1>(int_) | "C2:" << attr_cast<C2>(int_), c2) << "\n";

Which prints

C1:3
C2:5

Alternative 2: with a grammar/rules

typedef boost::variant<C1,C2> Var;

template<typename Iterator>
struct Generator: public karma::grammar<Iterator,Var()>
{
    Generator(): Generator::base_type(start)
    {
        using namespace karma;

        start = "grammar: " << (c1 | c2);
        c1 = "C1:" << attr_cast<int>(int_);
        c2 = "C2:" << attr_cast<int>(int_);
    }

  private:
    karma::rule<Iterator,Var()> start;
    karma::rule<Iterator,C1()> c1;
    karma::rule<Iterator,C2()> c2;
};

A full sample that shows both alternatives with out is here: http://liveworkspace.org/code/JWB9B$0:

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

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

struct C1 { int Getter() const { return 3; } void Setter(int){} }; 
struct C2 { int Getter() const { return 5; } void Setter(int){} }; 

BOOST_FUSION_ADAPT_ADT(C1, (int,int,obj.Getter(),obj.Setter(val)));
BOOST_FUSION_ADAPT_ADT(C2, (int,int,obj.Getter(),obj.Setter(val)));

typedef boost::variant<C1,C2> Var;

template<typename Iterator>
struct Generator: public karma::grammar<Iterator,Var()>
{
    Generator(): Generator::base_type(start)
    {
        using namespace karma;

        start = "grammar: " << (c1 | c2);
        c1 = "C1:" << attr_cast<int>(int_);
        c2 = "C2:" << attr_cast<int>(int_);
    }

  private:
    karma::rule<Iterator,Var()> start;
    karma::rule<Iterator,C1()> c1;
    karma::rule<Iterator,C2()> c2;
};

typedef boost::spirit::ostream_iterator It;

int main()
{
    C1 c1;
    C2 c2;

    using namespace karma;
    std::cout << karma::format("C1:" << attr_cast<C1>(int_) | "C2:" << attr_cast<C2>(int_), c1) << "\n";
    std::cout << karma::format("C1:" << attr_cast<C1>(int_) | "C2:" << attr_cast<C2>(int_), c2) << "\n";

    // or using a grammar:
    Generator<It> bla;
    std::cout << karma::format(bla, Var(c1)) << "\n";
    std::cout << karma::format(bla, Var(c2)) << "\n";
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Is `attr_cast` what allows you to sidestep the problem with the local reference described [here](https://svn.boost.org/trac/boost/ticket/6126)? –  Feb 22 '13 at 17:06
  • @llonesmiz Yeah. I "re-discovered" that limitation (bug) today, which lead me to post no example of it initially :). I've known that for quite a while (I'm bugssehe there in the tracker). AFAICT the attr_cast thingie indeed prevents trouble, both related to single-element fusion sequence adaptors as well as the local reference problem. I hear Spirit X3 attribute handling fixes most of the gritty cases and promises to be more consistent, as you can find out in [this interesting conversation](http://boost.2283326.n4.nabble.com/Error-with-container-within-sequence-tc4642517.html). – sehe Feb 22 '13 at 20:53
  • The essence seems to be that `attr_cast` separates attribute consumption from proxy-evaluation, at the cost of 1 explicit attribute copy. – sehe Feb 22 '13 at 20:54
  • Very interesting. Thanks as always for your answers. I like this way to solve it a lot more than using semantic actions. –  Feb 22 '13 at 20:55
  • @llonesmiz Likewise, sir! You teach me a lot. [Cheers](http://chat.stackoverflow.com/transcript/10?m=7813374#7813374)! – sehe Feb 22 '13 at 20:56