Разделение парсеров Boost.Spirit.X3 на несколько TU - PullRequest
0 голосов
/ 13 января 2020

Я снова борюсь с Boost.Spirit.X3.

У меня есть несколько логических групп анализаторов (операторов, выражений и т. Д. c.), Каждая из которых представлена ​​несколькими файлами:

  • group.hpp - содержит typedef s, BOOST_SPIRIT_DECLARE и extern объявление переменной для тех парсеров, которые используются "снаружи"
  • group_def.hpp - включает в себя предыдущее и содержит фактические определения парсеров, BOOST_SPIRIT_DEFINE, et c .
  • group.cpp - включает в себя предыдущий и содержит явные экземпляры шаблонов (через BOOST_SPIRIT_INSTANTIATE)

По сути, он более или менее соответствует структуре, предложенной в официальном учебник . Разница лишь в том, что моя грамматика намного сложнее, поэтому я пытаюсь разбить ее на несколько единиц перевода. Все эти TU затем скомпилированы в одну библиотеку, которая, в свою очередь, связана затем с основным исполняемым файлом.

Я попытался сделать «минимальный» пример здесь (жить на Wandbox), так как было бы неудобно перечислять здесь все файлы. Это не работает, хотя из-за некоторых нерешенных внешних факторов, так что, скорее всего, я что-то инстанцировал неправильно, но я уже потратил на это около недели, так что я совершенно отчаялся и не чувствую, что я могу справиться с этим самостоятельно.

Несколько вопросов и ответов:

Почему я предпочитаю использовать три файла на «группу»?

Во-первых, потому что я пытался сделать это таким образом, чтобы я Я не хочу перекомпилировать все при каких-либо небольших изменениях (не уверен, что мне это удалось), поэтому идея состоит в том, что somegroup.hpp - это просто «легкий» заголовок с объявлениями, somegroup_def.hpp - это заголовок с определениями, а somegroup.cpp просто используется для создания единицы перевода.

Во-вторых, я разделяю _def.hpp и .cpp, потому что я также включаю эти _def.hpp -файлы непосредственно в тесты, где я охватываю не только extern парсеры, но также и "внутренние" вспомогательные.

Почему я использую extern переменные?

Я попробовал это также с функциями, которые вместо этого возвращают парсеры (аналогично тому, как это делается в учебнике). По сути, именно так это реализовано и работает сейчас. Мне это не нравится, потому что, например, учитывая синтаксический анализатор lang::parser::import, я должен либо дать функции другое имя (lang::parser::import_), либо поместить ее в другое пространство имен (то есть lang::import). Также мне нравится способ использовать парсеры напрямую, как это делается в самом Духе (то есть без скобок: import против import_()).

Мои настоящие вопросы следующие:

  • Как правильно организовать структуру, если я хочу распределить свои парсеры по нескольким единицам перевода?
  • И что именно Я пропустил пример кода выше, чтобы он не связывался?

Буду признателен за любую помощь.

1 Ответ

2 голосов
/ 21 января 2020

Почему я предпочитаю использовать три файла на «группу»?

Я сам считаю разделение на grammar_def.hpp и grammar.cpp бесполезным, но это всего лишь мнение .

Почему я использую внешние переменные?

Я пробовал это также с функциями, которые вместо этого возвращают парсеры

Не делайте этого. Это приведет к фиаско инициализации порядка * stati c. Заполнители правил достаточно легки, чтобы создавать их экземпляры в каждой единице перевода.

Как правильно организовать структуру, если я хочу распределить свои парсеры по нескольким единицам перевода?

Что это вопрос о вкусах. Все, что вам нужно, это создать экземпляр правила в одном из TU и иметь x3::rule<...> + BOOST_SPIRIT_DECLARE в каждом другом, который его использует.

Лучший способ добиться этого - разделить x3::rule<...> off .cpp / _def.hpp в отдельный заголовок (поместите его в свой «легкий» .hpp) и включите его в каждый TU, для которого требуются эти правила.

См. https://github.com/mapnik/mapnik/pull/4072/files и https://github.com/boostorg/spirit/pull/493/files

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

  1. Вы смешиваете std::string::const_iterator и std::string::iterator итераторов.
  2. Вы используете некоторые из своих шкиперов в качестве парсеров (например, document_def = eols >> +megarule >> eols), но не создаете их экземпляры с надлежащим контекстом. Или просто не устанавливайте их как правила, или добавьте BOOST_SPIRIT_INSTANTIATE с контекстом, который вы видите в сообщении об ошибке.
...