Boost Spirit X3 не может скомпилировать директиву повтора с переменным фактором

Я пытаюсь использовать повторение директивы Boost Spirit X3 с коэффициентом повторения, который является переменным. Основная идея заключается в том, что заголовок + полезная нагрузка, где заголовок определяет размер полезной нагрузки. Простой пример "3 1 2 3" интерпретируется как header = 3, data= {1, 2, 3} (3 целых числа).

Я мог только найти примеры из документации Spirit Qi. В ней используется ссылка на усиление Феникса, чтобы обернуть переменный фактор: http://www.boost.org/doc/libs/1_50_0/libs/spirit/doc/html/spirit/qi/reference/directive/repeat.html

std::string str;
int n;
test_parser_attr("\x0bHello World",
    char_[phx::ref(n) = _1] >> repeat(phx::ref(n))[char_], str);
std::cout << n << ',' << str << std::endl;  // will print "11,Hello World"

Я написал следующий простой пример для Spirit X3 без удачи:

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <iostream>

namespace x3 = boost::spirit::x3;
using x3::uint_;
using x3::int_;
using x3::phrase_parse;
using x3::repeat;
using x3::space;
using std::string;
using std::cout;
using std::endl;

int main( int argc, char **argv )
{
  string data("3 1 2 3");
  string::iterator begin = data.begin();
  string::iterator end = data.end();

  unsigned int n = 0;

  auto f = [&n]( auto &ctx ) { n = x3::_attr(ctx); };
  bool r = phrase_parse( begin, end, uint_[f] >> repeat(boost::phoenix::ref(n))[int_], space );
  if ( r && begin == end  )
    cout << "Parse success!" << endl; 
  else
    cout << "Parse failed, remaining: " << string(begin,end) << endl;

  return 0;
}

Компиляция приведенного выше кода с boost 1.59.0 и clang++ (флаги: -std= C++14) дает следующее:

boost_1_59_0/boost/spirit/home/x3/directive/repeat.hpp:72:47: error: no matching constructor for

      initialization of 'proto_child0' (aka 'boost::reference_wrapper<unsigned int>')

            typename RepeatCountLimit::type i{};

Если я жестко repeat(3) вместо repeat(boost::phoenix::ref(n)) он работает должным образом, но это не является возможным решением, поскольку он должен поддерживать переменный коэффициент повторения.

Компиляция с repeat(n) завершается успешно, но не может выполнить синтаксический анализ со следующим выводом:“Parse failed, remaining: 1 2 3"

Глядя на исходный код для boost/spirit/home/x3/directive/repeat.hpp:72 он вызывает пустой конструктор для типа шаблона RepeatCountLimit::type переменная i и затем назначьте во время цикла for, повторяя мин и макс. Однако, поскольку тип является ссылкой, он должен быть инициализирован в конструкторе, поэтому компиляция не удалась. Глядя на эквивалентный исходный код из предыдущей версии библиотеки boost/spirit/home/qi/directive/repeat.hpp:162, он назначается напрямую:

        typename LoopIter::type i = iter.start();

Я не уверен, что я делаю здесь неправильно, или если x3 в настоящее время не поддерживает переменные факторы повторения. Буду признателен за помощь в решении этой проблемы. Спасибо.

1 ответ

Решение

Из того, что я понял, читая исходный код и список рассылки, Phoenix вообще не интегрирован в X3: причина в том, что C++14 делает большую часть этого устаревшим.

Я согласен, что это оставляет несколько мест, где у Ци были элегантные решения, например eps(DEFERRED_CONDITION), lazy(*RULE_PTR) ( трюк с Набиалеком), и действительно, это дело.

Spirit X3 все еще находится в разработке, поэтому мы можем увидеть это добавленным

На данный момент у Spirit X3 есть одно обобщенное средство для контекстного состояния. Это существенно заменяет locals<>, в некоторых случаях унаследованные аргументы, и могут быть / сделаны для / проверки количества элементов в этом конкретном случае:

  • x3::with ²

Вот как вы можете использовать это:

with<_n>(std::ref(n)) 
    [ omit[uint_[number] ] >> 
    *(eps [more] >> int_) >> eps [done] ]

Вот, _n тип тега, который идентифицирует элемент контекста для поиска с get<_n>(cxtx),

Обратите внимание, что в настоящее время мы должны использовать reference-wrapper для lvalue n так как with<_n>(0u) приведет к постоянному элементу внутри контекста. Я полагаю, что это тоже QoI, который может быть отменен, когда X# созревает

Теперь о семантических действиях:

unsigned n;
struct _n{};

auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); };

Это сохраняет проанализированный номер без знака в контексте. (На самом деле, из-за ref(n) привязка это на самом деле не является частью контекста, как уже упоминалось)

auto more   = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) >  _val(ctx).size(); };

Здесь мы проверяем, что мы на самом деле не "полный" - то есть допускается больше целых чисел

auto done   = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); };

Здесь мы проверяем, что мы "полны" - то есть больше не допускаются целые числа.

Собираем все вместе:

Жить на Колиру

#include <string>
#include <iostream>
#include <iomanip>

#include <boost/spirit/home/x3.hpp>

int main() {
    for (std::string const input : { 
            "3 1 2 3", // correct
            "4 1 2 3", // too few
            "2 1 2 3", // too many
            // 
            "   3 1 2 3   ",
        })
    {
        std::cout << "\nParsing " << std::left << std::setw(20) << ("'" + input + "':");

        std::vector<int> v;

        bool ok;
        {
            using namespace boost::spirit::x3;

            unsigned n;
            struct _n{};

            auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); };
            auto more   = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) >  _val(ctx).size(); };
            auto done   = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); };

            auto r = rule<struct _r, std::vector<int> > {} 
                  %= with<_n>(std::ref(n)) 
                        [ omit[uint_[number] ] >> *(eps [more] >> int_) >> eps [done] ];

            ok = phrase_parse(input.begin(), input.end(), r >> eoi, space, v);
        }

        if (ok) {
            std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout << v.size() << " elements: ", " "));
        } else {
            std::cout << "Parse failed";
        }
    }
}

Какие отпечатки:

Parsing '3 1 2 3':          3 elements: 1 2 3 
Parsing '4 1 2 3':          Parse failed
Parsing '2 1 2 3':          Parse failed
Parsing '   3 1 2 3   ':    3 elements: 1 2 3 

¹ одолжите свою поддержку / голос в списке рассылки [spirit-general]:)

² не могу найти подходящую ссылку на документацию, но она используется в некоторых примерах

Другие вопросы по тегам