将 Boost.Spirit.X3 解析器拆分为多个 TU

Splitting Boost.Spirit.X3 parsers into several TUs

我再次与 Boost.Spirit.X3 作斗争。

我有几个解析器逻辑组(语句、表达式等),每个解析器由几个文件表示:

基本上,它或多或少遵循了官方提出的结构tutorial。唯一的区别是我的语法要复杂得多,所以我试图将它分成几个翻译单元。然后将所有这些 TU 编译成一个库,然后 linked 到主要的可执行文件中。

我尝试制作一个 "minimal" 示例 here(在 Wandbox 上运行),因为在这里列出所有文件不方便。 它不起作用 因为一些未解决的外部问题,所以,很可能,我错误地实例化了一些东西,但我已经为此花了大约一个星期的时间,所以我非常绝望而且我觉得我无法独自处理这件事。

几个问题及答案:

为什么我更喜欢每个 "group" 使用三个文件?

首先,因为我试图以这样一种方式进行制作,所以我不想在任何小改动上重新编译所有内容(不确定我是否成功),所以我的想法是 somegroup.hpp只是带有声明的 "lightweight" header,somegroup_def.hpp 是带有定义的 header,而 somegroup.cpp 仅用于创建翻译单元。

其次,我将 _def.hpp.cpp 分开,因为我还将这些 _def.hpp 文件直接包含到测试中,我不仅涵盖 extern 解析器,还涵盖 "internal"辅助的。

为什么我要使用 extern 个变量?

我也尝试使用 return 解析器的函数(类似于教程中的做法)。基本上,这就是它现在的实现方式和工作方式。我不喜欢它,因为,例如,给定一个解析器 lang::parser::import,我必须给函数另一个名称 (lang::parser::import_) 或将它放在另一个名称空间中(即 lang::import) .此外,我喜欢直接使用解析器的方式,它是如何在 Spirit 本身中完成的(即没有括号:importimport_())。

我的实际问题如下:

如有任何帮助,我将不胜感激。

Why do I prefer using three files per "group"?

我自己觉得分成 grammar_def.hppgrammar.cpp 没用,但这只是一个意见。

Why am I using extern variables?

I tried it also with functions that return parsers instead

不要这样做。它将导致静态初始化顺序的失败。规则占位符足够轻便,可以在每个翻译单元中实例化它们。

How to properly organize the structure if I want to spread my parsers over several translation units?

这是一个关于品味的问题。您所需要的只是在其中一个 TU 中实例化一个规则,并在使用它的每个其他 TU 中都有一个 x3::rule<...>+BOOST_SPIRIT_DECLARE

实现这一目标的最佳方法是将 x3::rule<...>.cpp/_def.hpp 拆分为单独的 header(将其放入您的 "lightweight" .hpp),并将其包含在需要这些规则的每个 TU 中。

https://github.com/mapnik/mapnik/pull/4072/files and https://github.com/boostorg/spirit/pull/493/files

And what exactly am I missing in the example of code above, so that it doesn't link?

  1. 您正在混合使用 std::string::const_iteratorstd::string::iterator 迭代器。
  2. 您正在使用一些船长作为解析器 (f.ex。document_def = eols >> +megarule >> eols),但没有使用适当的上下文实例化它们。要么简单地不让它们成为规则,要么添加 BOOST_SPIRIT_INSTANTIATE 以及您在错误消息中看到的上下文。