2

This has been plaguing me for hours, and I do not understand how to make it work. I simply want to do something each time a rule is used, in this example increment a counter. If I do not explicitly specify the rule but use it in the call to boost::spirit::karma::generate, it works. But when I try to put everything into a rule, it will not compile, and I do not get any insight from the lengthy error message.

#include <iostream>
#include <string>

#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

int main()
{
    using boost::spirit::karma::eps;
    using boost::spirit::karma::int_;
    using boost::spirit::karma::lit;
    using boost::spirit::karma::eol;
    using boost::phoenix::val;
    using boost::phoenix::ref;
    using boost::spirit::karma::generate;
    using boost::spirit::karma::rule;

    typedef std::back_insert_iterator<std::string> OutputIteratorType;

    std::string s;
    std::back_insert_iterator<std::string> sink(s);
    int lineNum = 0;

    generate(sink, eps[ref(lineNum) += 10] << lit("Line number ") << lit(lineNum) << lit(": ") << int_ << eol, 123);
    generate(sink, eps[ref(lineNum) += 10] << lit("Line number ") << lit(lineNum) << lit(": ") << int_ << eol, 123);

    // Will not compile
    //rule<OutputIteratorType, int()> testRule = eps[ref(lineNum) += 10] << lit("Line number ") << lit(lineNum) << lit(": ") << int_ << eol;
    //generate(sink, testRule, 123);
    //generate(sink, testRule, 123);

    std::cout << s;
    return 0;
}

(Above you can see the boost::spirit version which is most elegant, but using a lambda function or a member function all results in the same, the "direct method" works, the "rule method" doesn't.)

Unfortunately I also cannot find any documentation or examples or other resources that cover this, I'd be very thankful for references too.

Xoph
  • 459
  • 2
  • 10

1 Answers1

3

That is a problem with boost::phoenix V2 (don't ask which ;-)) So, using V3 will work.

Additionally one has to give the attribute to the int generator and reference the lineNum when printing it.

#include <iostream>
#include <string>

#define BOOST_SPIRIT_USE_PHOENIX_V3

#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

int main() {
  using boost::spirit::karma::eps;
  using boost::spirit::karma::int_;
  using boost::spirit::karma::lit;
  using boost::spirit::karma::eol;
  using boost::spirit::karma::_1;
  using boost::spirit::karma::_val;
  using boost::phoenix::val;
  using boost::phoenix::ref;
  using boost::spirit::karma::generate;
  using boost::spirit::karma::rule;

  typedef std::back_insert_iterator<std::string> OutputIteratorType;

  std::string s;
  std::back_insert_iterator<std::string> sink(s);
  int lineNum = 0;

  rule<OutputIteratorType, int()> testRule = eps[ref(lineNum) += 10]
      << lit("Line number ") << lit(ref(lineNum)) << lit(": ")
      << int_[_1 = _val] << eol;
  generate(sink, testRule, 123);
  generate(sink, testRule, 123);

  std::cout << s;
  return 0;
}
Mike M
  • 2,263
  • 3
  • 17
  • 31
  • The code you gave even compiles with spirit V2, so giving the attribute to the int_ generator was the crucial step. (I missed referencing the lineNum but it's clear to me why that is necessary.) Could you explain why modifying the int_ generator is necessary? I still have trouble getting this to work with my actual rules, not this test case, since I have a more complicated rule following, and unfortunately appending "[_1 = _val]" doesn't help there. – Xoph Aug 09 '13 at 10:31
  • 1
    Maybe %= also helps, like cv_and_he said above. Otherwise you can just factor your rule out, then appending `[_1 = _val]` will do agian, and the type of this element will be the type of the rule you just out factored. You could also assign values to each rule via `_2, _3, _4, ...` and provide them with `phx::at_c(_val)`. – Mike M Aug 09 '13 at 10:37
  • I actually can make my rules work now, awesome, thanks! I had missed that [] binds more strongly than +, so a pair of parentheses made the "[_1 = _val]" magic work. However, I still have no idea why this is necessary, and I probably would never have figured that out on my own. So an explanation would be greatly appreciated! – Xoph Aug 09 '13 at 10:39
  • I guess you have to read about attribute propagation in spirit qi and karma... I'm also still learning here all the quirks... – Mike M Aug 09 '13 at 10:40
  • 2
    @Xoph [This](http://boost-spirit.com/home/articles/attribute_handling/attribute-propagation-and-attribute-compatibility/) may be of interest. – llonesmiz Aug 09 '13 at 10:45