Я снова борюсь с 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_()
).
Мои настоящие вопросы следующие:
- Как правильно организовать структуру, если я хочу распределить свои парсеры по нескольким единицам перевода?
- И что именно Я пропустил пример кода выше, чтобы он не связывался?
Буду признателен за любую помощь.