базовый дух буста семантическое действие не компилируется

Я пытаюсь добавить оператор больше, чем > асту: код на 95% идентичен коду в документации.

Две точки интереса ниже

  • Блок кода, в котором я пытаюсь написать поддержку «больше чем», прокомментирован в коде ниже.
  • Одна строка в разборе для term который не компилируется, потому что я еще не понимаю семантические действия: не уверен, как связать lhs из lhs > rhs через феникса и семантические действия.

Решение должно быть тривиальным для постоянных пользователей Духа, но я все еще учусь и пока только пробую примеры.

Любая помощь будет оценена. ТИА.

Код

#define BOOST_SPIRIT_DEBUG

#include <boost/spirit/include/qi.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/classic_symbols.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/regex.hpp>   // std::regex not fully implemented in stdc++ yet

#include <string>
#include <map>
#include <utility>
#include <functional>
#include <iostream>
#include <string>
#include <vector>

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

struct binary_op;
struct unary_op;
struct nil {};

struct expression_ast
{
typedef
boost::variant<
nil // can't happen!
, double
, std::string
, boost::recursive_wrapper<expression_ast>
, boost::recursive_wrapper<binary_op>
, boost::recursive_wrapper<unary_op>
>
type;

expression_ast()
: m_expr(nil()) {}

template <typename Expr>
expression_ast(Expr const& expr)
: m_expr(expr) {}

expression_ast& operator+=(expression_ast const& rhs);
expression_ast& operator-=(expression_ast const& rhs);
expression_ast& operator*=(expression_ast const& rhs);
expression_ast& operator/=(expression_ast const& rhs);type m_expr;
};

struct binary_op
{
binary_op(
char op
, expression_ast const& left
, expression_ast const& right)
: m_op(op), m_left(left), m_right(right) {}

char m_op;
expression_ast m_left;
expression_ast m_right;
};

struct unary_op
{
unary_op(
char op
, expression_ast const& subject)
: m_op(op), m_subject(subject) {}

char m_op;
expression_ast m_subject;
};

expression_ast& expression_ast::operator+=(expression_ast const& rhs)
{
m_expr = binary_op('+', m_expr, rhs);
return *this;
}

expression_ast& expression_ast::operator-=(expression_ast const& rhs)
{
m_expr = binary_op('-', m_expr, rhs);
return *this;
}

expression_ast& expression_ast::operator*=(expression_ast const& rhs)
{
m_expr = binary_op('*', m_expr, rhs);
return *this;
}

expression_ast& expression_ast::operator/=(expression_ast const& rhs)
{
m_expr = binary_op('/', m_expr, rhs);
return *this;
}

// We should be using expression_ast::operator-. There's a bug
// in phoenix type deduction mechanism that prevents us from
// doing so. Phoenix will be switching to BOOST_TYPEOF. In the
// meantime, we will use a phoenix::function below:
struct negate_expr
{
template <typename T>
struct result
{
typedef T type;
};

expression_ast operator()(expression_ast const& expr) const
{
return expression_ast(unary_op('-', expr));
}
};

static boost::phoenix::function<negate_expr> neg;

struct ast_print
{
typedef std::string result_type;

std::string operator()(qi::info::nil) const
{
return "";
}
std::string operator()(std::string const& str) const
{
return str;
}
std::string operator()(double d) const
{
std::ostringstream oss;
oss << d;
return oss.str();
}

std::string operator()(expression_ast const& ast) const
{
return boost::apply_visitor(*this, ast.m_expr);
}

std::string operator()(binary_op const& expr) const
{
std::ostringstream oss;
oss << "op:" << expr.m_op << "(";
oss << boost::apply_visitor(*this, expr.m_left.m_expr);
oss << ", ";
oss << boost::apply_visitor(*this, expr.m_right.m_expr);
oss << ')';
return oss.str();
}

std::string operator()(unary_op const& expr) const
{
std::ostringstream oss;
oss << "op:" << expr.m_op << "(";
oss << boost::apply_visitor(*this, expr.m_subject.m_expr);
oss << ')';
return oss.str();
}
};

std::ostream& operator << (std::ostream& stream, const expression_ast& expr)
{
ast_print printer;
stream << printer(expr) << std::endl;
return stream;
}

// CODE ADDED HERE ------------------------------------------------------------
template< char OP >
struct binary_expr
{
template <typename T>
struct result
{
typedef T type;
};

expression_ast operator()(expression_ast const& lhs,expression_ast const& rhs) const
{
return expression_ast(binary_op( OP, lhs, rhs ));
}
};

static boost::phoenix::function<binary_expr<'>'>> gt;
// CODE ADDED END HERE -------------------------------------------------------

template <typename Iterator>
struct ParserGenerator : qi::grammar<Iterator, expression_ast(), ascii::space_type>
{
ParserGenerator() : ParserGenerator::base_type(expression)
{
using qi::_val;
using qi::_1;
using qi::double_;
using qi::iso8859_1::char_;
using qi::iso8859_1::space;
using qi::eol;
using boost::spirit::ascii::string;

comment =
space >> ("//" >> *(char_ - eol) >> eol)
;

expression =
term                            [_val = _1]
>> *(   ('+' >> term            [_val += _1])
|   ('-' >> term            [_val -= _1])
)
;

term =
factor                          [_val = _1]
>> *(   ('*' >> factor          [_val *= _1])
|   ('/' >> factor          [_val /= _1])
//          |   ('>' >> factor          [_val = gt(qi::_val,_1)]) // PROBLEM HERE!
)
;

factor =
symbol                          [_val = _1]
| double_                       [_val = _1]
|   '(' >> expression           [_val = _1] >> ')'
|   ('-' >> factor              [_val = neg(_1)])
|   ('+' >> factor              [_val = _1])
;

symbol %=
(symbol_raw
>> *( string("[") >> +qi::digit >> string("]"))
>> *( string(".") >> symbol ))
;

symbol_raw %=
+(qi::alpha | qi::char_( "_" ))
;

BOOST_SPIRIT_DEBUG_NODE(expression);
BOOST_SPIRIT_DEBUG_NODE(term);
BOOST_SPIRIT_DEBUG_NODE(factor);
BOOST_SPIRIT_DEBUG_NODE(comment);
BOOST_SPIRIT_DEBUG_NODE(symbol);
BOOST_SPIRIT_DEBUG_NODE(symbol_raw);
}

qi::rule<Iterator, expression_ast(), ascii::space_type>
expression, term, factor, comment;

qi::rule<Iterator, std::string(), ascii::space_type>
symbol, symbol_raw;
};
}

int main(int argc, char* argv[])
{
using boost::spirit::ascii::space;
using client::expression_ast;
using client::ast_print;

typedef std::string::const_iterator iterator_type;
typedef client::ParserGenerator<iterator_type> ParserGenerator;

ParserGenerator pg;   // our grammar
std::string predicate( "i_.c>x[0]" );
expression_ast  ast;
ast_print       printer;

iterator_type iter = predicate.begin(), end = predicate.end();
if ( phrase_parse( iter, end, pg, space, ast ))
{
std::cerr << printer( ast ) << std::endl;
}

return 0;
}

2

Решение

TL; DR использовать

template <typename, typename> struct result { typedef expression_ast type; };

внутри binary_expr структура. Вот почему:


Вы объявляете объект функтора, который будет использоваться как Феникс ленивый актер.

Функтор это то, что известно как Отложенный / Полиморфный Убываемый Объект (PCE) в документации Boost. Это означает, что шаблоны выражений, создающие актеров Phoenix, фактически будут выполнять только фактическое разрешение перегрузки / вычитание типа для аргументов функции. в момент подачи заявки.

Тип возврата функции не могу быть выведено (как это не может быть в обычном (не ленивом) C ++). Здесь библиотеки повышения используют протокол BOOST_RESULT_OF.

Заметка Протокол RESULT_OF является устаревшим / избыточным при использовании C ++ 11, так как C ++ 11 имеет decltype, Чтобы включить это, на большинстве компиляторов вам нужно

#define BOOST_RESULT_OF_USE_DECLTPYE

(хотя примечания к выпуску v1_52_0 дают понять, что decltype становится стандартным для компиляторов, которые поддерживают его достаточно хорошо).

Что влечет за собой этот протокол, это:

  • Когда вы используете полиморфный вызываемый объект с n параметры, boost будет искать вложенный type ЬурейеЕ внутри вложенный шаблон класса resultпараметризованный с n фактические типы аргументов. Это будет тип возврата
  • Фактический вызов функтора звонки operator() с этими аргументами. Здесь C ++ будет оставлено для разрешения перегрузки.

В общем, вы должны написать свой функтор в полностью параметризованном стиле:

template<char OP>
struct binary_expr
{
template <typename, typename> struct result { typedef expression_ast type; };

template <typename A, typename B>
typename result<A,B>::type operator()(A const&a,B const&b) const {
return expression_ast(binary_op( OP, a, b ));
}
};

Это работает. Однако, поскольку фактическое разрешение перегрузки типов аргументов выполняется компилятором, вы можете изменить operator() подпись для использования фиксированных типов:

template <typename A, typename B>
expression_ast operator()(A const&a,B const&b) const {
return binary_op(OP, a, b);
}

Если вы хотите, вы можете покончить с (некоторыми) параметрами шаблона:

template <typename E>
expression_ast operator()(E const&a,E const&b) const {
return binary_op(OP, a, b);
}

Или даже не шаблон функции вообще:

expression_ast operator()(expression_ast const&,expression_ast const&) const;

Все отлично, до тех пор, пока одна перегрузка соответствует фактический Типы аргументов переданы. Важный бит, хотя это то, что result<...>::type оценивается независимо от подписи operator(), и как таковой для этого потребуется точное число ожидаемых аргументов.

Также обратите внимание, что вы можете комбинировать функторы разной арности:

template <char OP> struct operator_expr
{
template <typename T, typename=T> struct result
{ typedef expression_ast type; };

expression_ast operator()(expression_ast const& expr) const
{ return expression_ast(unary_op(OP, expr)); }

expression_ast operator()(expression_ast const&a, expression_ast const&b) const
{ return binary_op(OP, a, b); }
};

static boost::phoenix::function<operator_expr<'-'>> neg;
static boost::phoenix::function<operator_expr<'>'>> gt;

Это работает, потому что оба result<T>::type а также result<T,U>::type допустимые выражения типа с этим определением.


Бонусные заметки:

  1. У тебя была странность там, где ты сказал

    template <typename T> struct result { typedef T type; };
    

    вместо

    template <typename> struct result { typedef expression_ast type; };
    

    Это потому, что возвращаемый тип на самом деле не варьируется в зависимости от фактического типа аргумента. В вашем примере тип аргумента было бы как правило, то же самое, но технически это не имеет смысла.

  2. Вы можете делать вещи без протокола BOOST_RESULT_OF, если вы используете decltype. Это означает, что вы можете просто оставить вложенный result структура

  3. Вы также можете адаптировать обычные функции в качестве актеров Феникса:

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    
    // ...
    
    expression_ast neg_expr(expression_ast const&a)                         { return unary_op ('-', a); }
    expression_ast gt_expr (expression_ast const&a, expression_ast const&b) { return binary_op('>', a, b); }
    
    BOOST_PHOENIX_ADAPT_FUNCTION(expression_ast, neg, neg_expr, 1)
    BOOST_PHOENIX_ADAPT_FUNCTION(expression_ast, gt,  gt_expr,  2)
    

    Это будет в основном писать объекты функтора для тебя, включая биты протокола RESULT_OF.

  4. Наконец, вы можете использовать стандартные актеры Феникса вместо определения пользовательских. В этом случае нет необходимости в любом из вышеперечисленных:

    using phx = boost::phoenix;
    // ...
    |   ('>' >> factor [_val = phx::construct<binary_op>('>', _val, _1)]) // PROBLEM HERE!
    // ...
    |   ('-' >> factor [_val = phx::construct<unary_op>('-', _1)])
    

Заворачивать

Полный код здесь: http://ideone.com/Xv9IH1 и был проверен на

  • MSVC 2012, boost 1_52_0, Win64
  • GCC 4.8, boost 1_52_0, Win64
7

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]