передать атрибут дочернего правила в духе буста

У меня есть два правила с одним и тем же атрибутом.

Можно ли передать атрибут правила matrix_ дочернему правилу matrixBlock_?
Я хочу сохранить директиву повтора от создания атрибута вектора формы< >. Вместо этого он должен просто продолжать запись в атрибут matrix_ (времена numBlocks).
Я попытался передать атрибут как унаследованный атрибут в дочернее правило, и он компилируется (см. Ниже). Но я получаю несколько «призрачных» записей в моем векторе, которые происходят не от phoenix :: push_back.
Также это, кажется, не оптимальный путь для меня. Возможно ли нам автоматическое распространение атрибутов в matrixBlock_ вместо семантических действий?

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ matrixBlock_(_val) ];
matrixBlock_ = *column[phoenix::push_back(_r1, _1)];

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
qi::rule<Iterator, void(Matrix&), ascii::space_type> matrixBlock_;

Обновить

уточнить вопрос:

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

vector< vector< columnT > >

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ matrixBlock_ ];
matrixBlock_ = *column;

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
qi::rule<Iterator, Matrix(), ascii::space_type> matrixBlock_;

Я хочу, чтобы он имел тот же тип атрибута, что и matrixBlock_, одномерный массив.


Мое реальное решение — использовать только одно правило. (выглядит легко :-))

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ *column_[ phoenix::push_back(_val, _1) ] ];
//matrixBlock_ = *column;

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
//qi::rule<Iterator, Matrix(), ascii::space_type> matrixBlock_;

Обновить

Я смог воспроизвести фантомные записи с этим кодом в vs2010 и повысить 1.46.1

http://liveworkspace.org/code/505091dc4631a379763567168a728e0c

Вывод был: 42, 45, -9, 3, 2, 1, 12, 34, 56, 0, 0, 0

Моя ошибка заключалась в использовании старой версии Boost. Нет фонтомов с 1.5.

Теперь у меня есть две рабочие версии моей грамматики. Можно ли изменить грамматику без использования семантического действия push_back?

2

Решение

Отвечая на ваш отредактированный вопрос: Да, вы можете сделать это без семантических действий, просто:

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
Parser() : Parser::base_type(matrix_)
{
matrixBlock_ = qi::lit(";") >> *qi::int_;
matrix_      = qi::repeat(3)[ matrixBlock_ ];
}
qi::rule<It, Matrix(), qi::space_type> matrixBlock_, matrix_;
};

Обратите внимание, что вы может хочу проверить количество строк / столбцов. Смотрите мой расширенный образец, который использует дополнительное семантическое действие, чтобы проверить это (обратите внимание на тонкое изменение от *int_ в +int_ чтобы избежать пустых строк):

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>

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

typedef std::vector<int> Matrix;

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
Parser() : Parser::base_type(matrix_)
{
using namespace qi;
matrixBlock_ = lit(";") >> +int_ >> eps( 0 == (phx::size(_val) % 3));
matrix_      = repeat(3)[ matrixBlock_ ];
}
qi::rule<It, Matrix(), qi::space_type> matrixBlock_, matrix_;
};

int main()
{
std::string test = ";42 45 -9; 3 2 1; 12 34 56";

std::string::const_iterator f(test.begin()), l(test.end());

Parser<std::string::const_iterator> parser;
Matrix m;

if (qi::phrase_parse(f,l,parser,qi::space, m))
std::cout << "Wokay\n";
else
std::cerr << "Uhoh\n";

std::cout << karma::format(karma::auto_ % ", ", m) << "\n";
}

Старый ответ:

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

Вот простой пример, показывающий, как его использовать, live:

Примечание в отношении «фантомных записей» в целом:

Обратите внимание, что есть несколько часто задаваемых вопросов, связанных с грамматикой возврата и атрибутами контейнера. Дело в том, что по соображениям производительности парсеры не будут отменять («откатывать») изменения своих базовых контейнеров при возврате. Вы можете заставить это поведение, используя qi::hold но это может стоить усилий, чтобы изменить грамматику либо

  • избегать возврата или
  • зафиксировать атрибут на более поздней стадии (используя семантические действия)

Полный пример кода:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;

struct Matrix
{
std::vector<int> data;
};

namespace boost { namespace spirit { namespace traits {
template <>
struct is_container<Matrix>
{
};

template <typename Attrib>
struct push_back_container<Matrix, Attrib>
{
static bool call(Matrix& c, Attrib const& val)
{
c.data.push_back(val);
return true;
}
};

template <>
struct container_value<Matrix>
{
typedef int type;
};
} } }

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
Parser() : Parser::base_type(start)
{
start = *qi::int_;
}
qi::rule<It, Matrix(), qi::space_type> start;
};

int main()
{
std::string test = "42 45 -9";

std::string::const_iterator f(test.begin()),
l(test.end());

Parser<std::string::const_iterator> parser;
Matrix m;

if (qi::phrase_parse(f,l,parser,qi::space, m))
std::cout << "Wokay\n";
else
std::cerr << "Uhoh\n";

std::cout << karma::format(karma::auto_ % ", ", m.data) << "\n";
}

Выход:

Wokay
42, 45, -9

Обновить

Немного больше предыстории:

Конечно, для такого простого примера, как этот, который просто оборачивает стандартный поддерживаемый тип контейнера, было бы довольно легко использовать fusion приспособление вместо: ( http://liveworkspace.org/code/56aea8619867451a21cd49fddb1e93bd )

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

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;

struct Matrix { std::vector<int> data; };
BOOST_FUSION_ADAPT_STRUCT(Matrix, (std::vector<int>, data));

int main()
{
std::string test = "42 45 -9";
std::string::const_iterator f(test.begin()), l(test.end());

Matrix m;
if (qi::phrase_parse(f,l, qi::eps >> *qi::int_, qi::space, m))
std::cout << karma::format(karma::auto_ % ", ", m.data) << "\n";
}

Обратите внимание, что qi::eps необходим из-за ошибки (AFAICT) со структурами, которые содержат только один элемент данных. Смотрите, например обсуждение здесь (и некоторые другие упоминания)

4

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

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