Подтвердить что ты не робот

Копировать или ссылочную семантику правила boost:: spirit <>?

Я пытаюсь написать синтаксический анализатор оболочки в Boost.Spirit. Тем не менее, я не понимаю некоторые основные вопросы, касающиеся семантики rule s.

Посмотрите на документацию, есть члены r.alias() и r.copy() of rule. IIUC, эти члены должны возвращать ссылку на правило и копию содержимого правила соответственно. Однако четко не указано, что происходит, когда я просто использую правило в определении другого правила. Из моих экспериментов я нашел взаимно рекурсивные правила, которые можно определить:

rule<Iter> r1, r2;
r1 = ... >> r2 >> ...;
r2 = ... >> r1 >> ...;

который предполагает, что правила берутся по ссылке внутри выражений парсера. Проблема в том, что он делает, когда переменная выходит за пределы области видимости, например:

rule<Iter> r1;
{ 
  rule<Iter> r2;
  r1 = ... >> r2 >> ...;
  r2 = ... >> r1 >> ...;
}
... // use r1

В том же примечании будет назначаться правило из выражения синтаксического анализа, содержащего rvalue работы правила типа (r.copy() тоже будет rvalue типа rule), не так ли? например.

rule<Iter> f() { return char_('a') << char_('b'); }
rule<Iter> r1 = ... << f();

Может ли кто-нибудь просветить меня по подробной семантике копий и ссылок rule и, возможно, исправить любые заблуждения в этом сообщении?

4b9b3361

Ответ 1

Ответ зависит от того, какую версию Духа вы имеете в виду.


Spirit.Classic(бывший Spirit V1.x) реализует специальную семантику копирования для правил. В документации указано:

Когда правило ссылается где угодно правая сторона EBNF выражение, правило удерживается выражение по ссылке. Это ответственность клиента за обеспечение что указанное правило остается в область действия и не разрушается пока он ссылается.

Оператор присваивания по существу ссылается на правило rhs без создания глубокой копии. Это было сделано, чтобы:

rule<> r1, r2;
r1 = ...;
r2 = r1;

Но это оказалось очень путаным, так как это предотвратило обращение правил так же, как "нормальные" объекты.

По этой причине была функция-член rule::copy(), позволяющая делать явные глубокие копии правила (например, для хранения их в контейнере STL).

В то же время это:

r2 = r1.copy();

неверно. r2 будет ссылаться на (разрушенную) временную копию r1, возвращаемую функцией copy().


В Spirit.Qi(т.е. Spirit V2.x) поведение частично изменяется. теперь правила ведут себя так, как ожидалось, при обработке вне парсеров. Обычно их можно хранить в контейнерах (оператор присваивания отображает ожидаемое поведение). Но будьте осторожны, что внутри правил выражения парсера по-прежнему сохраняется ссылка, которая по-прежнему позволяет ссылаться на правило так же, как и раньше:

rule<> r1, r2;
r1 = ... >> r2 >> ...;
r2 = ... >> r1 >> ...;

Иногда необходимо сделать глубокую копию правила, так что все еще существует функция functon copy.

Измененная семантика копии имеет другой побочный эффект. Создает:

r1 = r2;

теперь создают (глубокую) копию r2, которая может быть не такой, какой вы ожидаете, особенно если r2 получит свой rhs, назначенный только после того, как "назначен" на r1. По этой причине существует новая функция-член alias, позволяющая использовать ссылочную семантику для этого углового случая:

r1 = r2.alias();

В любом случае, в обеих версиях Spirit вы получите оборванные ссылки, если часть правил, на которые ссылается выражение синтаксического анализа, выходит из области видимости.

BTW, ни версия Spirit не реализует функцию rule::ref().