3

I'm trying to compile a simple grammar using Boost.Spirit. I'm using g++ 4.7.0 and boost 1.49.0-1.1 on Arch Linux x86_64.

The eventual goal here is an assembler. There will be multiple operands with one class each. All the operand types together are stored in a boost::variant type.

I've had success compiling this sample up to the direct rule when it is also the base_type of the grammar, but introducing the operand rule (and making it the base type) causes g++ 4.7.0 to complain that:

example.cpp:61:7:   required from ‘Grammar<Iterator>::Grammar() [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >]’
example.cpp:76:21:   required from here
/usr/include/boost/spirit/home/qi/detail/attributes.hpp:23:63: error: no matching function for call to ‘DirectOperand::DirectOperand()’
/usr/include/boost/spirit/home/qi/detail/attributes.hpp:23:63: note: candidates are:
example.cpp:20:12: note: DirectOperand::DirectOperand(const DirectValue&)
example.cpp:20:12: note:   candidate expects 1 argument, 0 provided
example.cpp:16:7: note: DirectOperand::DirectOperand(const DirectOperand&)
example.cpp:16:7: note:   candidate expects 1 argument, 0 provided

I don't understand why it should be looking for a default constructor for DirectOperand, since the semantic action should call it with the constructor.

I've tried lots of variations, including

operand = directOp[_val = _1];

and even writing a helper function to "force" the type, like:

static Operand makeDirectOperand( const DirectOperand& op ) { return op; }

// ...

operand = directOp[&makeDirectOp];

but no matter what I do, it complains about the missing default constructor.

When I actually defined a zero-argument constructor, I found that it compiled, but that that DirectOperand::value_ never changed from the default value I assigned.

Here's the code. It's as short as I could make it.

#include <cstdint>
#include <iostream>
#include <string>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_uint.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/variant.hpp>

typedef std::uint16_t DataWord;
typedef boost::variant<std::string, DataWord> DirectValue;

class DirectOperand {
private:
  DirectValue value_;
public:
  explicit DirectOperand( const DirectValue& value ) :
  value_( value ) {}

  const DirectValue& value() const { return value_; }
};

// For example purposes, but there will be multiple operand types
// here.
typedef boost::variant<DirectOperand> Operand;

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

template <typename Iterator>
struct Grammar : qi::grammar<Iterator, Operand(), ascii::space_type> {
   Grammar() : Grammar::base_type( operand ) {
      using qi::lexeme;
      using ascii::char_;
      using qi::uint_parser;
      using namespace qi::labels;

      uint_parser<DataWord, 16, 1, 4> uhex_p;
      uint_parser<DataWord, 10, 1, 5> uint_p;

      word =
        char_( "a-zA-Z._" ) [_val += _1]
        >> *char_( "a-zA-Z0-9._" ) [_val += _1]
        ;

      number = (
        "0x" >> uhex_p
        | uint_p
        )
        [_val = _1]
        ;

      direct %= ( word | number );

      directOp %= direct;

      // This would be ( directOp | indirectOp | etc)
      operand %= directOp;
   }

  qi::rule<Iterator, DataWord(), ascii::space_type> number;
  qi::rule<Iterator, std::string()> word;
  qi::rule<Iterator, DirectValue(), ascii::space_type> direct;
  qi::rule<Iterator, DirectOperand(), ascii::space_type> directOp;
  qi::rule<Iterator, Operand(), ascii::space_type> operand;
};

int main() {
   std::string line;

   typedef std::string::iterator iterator_type;
   typedef Grammar<iterator_type> Grammar;
   Grammar grammar {};
}
jhaberku
  • 175
  • 7

1 Answers1

3

I believe that the instantiation of the attribute qi::rule (directOp here) requires a default constructor.

If you are loathe to include a default constructor in DirectOperand, you could try wrapping it up in a boost::optional for the purpose of deferring initialization.

sehe
  • 374,641
  • 47
  • 450
  • 633
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Yup, that was the problem. Boost.Optional solved the problem nicely. My comment in my question about garbage when the default constructor was called was actually due to lack of debugging symbols. – jhaberku Apr 14 '12 at 01:51
  • The 'optional' seems not working with x3 in some cases. Could you please explain what exactly dereferences optionals in Qi? – Igor R. Feb 07 '17 at 16:22
  • @IgorR.: Sorry, but I haven't worked with Qi for... close to 5 years now, so I have no idea :( – Matthieu M. Feb 07 '17 at 17:10
  • @sehe perhaps you could sched some light? – Igor R. Feb 07 '17 at 17:21
  • @IgorR. It's the `move_to` and other traits in `boost::spirit::x3`. If you have a specific case where "it's not working" I might be able to explain it or confirm a bug – sehe Feb 07 '17 at 17:47
  • @sehe sorry, even more trivial: http://melpon.org/wandbox/permlink/9Vsk4ZLJruy3T9k2 (in Qi optional attr used to get dereferenced somehow, didn't it?) – Igor R. Feb 07 '17 at 20:30