我又在为Boost.Spirit.X3而苦苦挣扎。
我有几个解析器的逻辑组(语句、表达式等(,每个逻辑组都由几个文件表示:
group.hpp
- 包含typedef
s、BOOST_SPIRIT_DECLARE
和extern
变量声明,用于"外部"使用的解析器group_def.hpp
- 包括前一个,并包含解析器、BOOST_SPIRIT_DEFINE
等的实际定义。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
vsimport_()
(。
我的实际问题如下:
- 如果我想将解析器分布在多个翻译单元上,如何正确组织结构?
- 在上面的代码示例中,我到底缺少什么,以至于它没有链接?
我将不胜感激任何帮助。
为什么我更喜欢每个"组"使用三个文件?
我自己也觉得分离成grammar_def.hpp
,grammar.cpp
没用,但这只是一种意见。
为什么我使用外部变量?
我也尝试了使用返回解析器的函数
不要这样做。这将导致静态初始化订单惨败。规则占位符足够轻量级,可以在每个翻译单元中实例化它们。
如果我想将解析器分布在多个翻译单元上,如何正确组织结构?
这是一个关于品味的问题。您所需要的只是在其中一个 TU 中实例化一个规则,并在使用它的所有其他规则中都有一个x3::rule<...>
+BOOST_SPIRIT_DECLARE
。
实现这一目标的最佳方法是将x3::rule<...>
从.cpp
/_def.hpp
拆分为单独的标头(将其放入您的"轻量级".hpp
中(,并将其包含在需要这些规则的每个 TU 中。
查看 https://github.com/mapnik/mapnik/pull/4072/files 和 https://github.com/boostorg/spirit/pull/493/files
在上面的代码示例中,我到底缺少什么,以至于它没有链接?
- 您正在混合
std::string::const_iterator
和std::string::iterator
迭代器。 - 您正在使用一些船长作为解析器(f.ex.
document_def = eols >> +megarule >> eols
(,但不要使用适当的上下文实例化它们。要么干脆不将它们设为规则,要么使用错误消息中看到的上下文添加BOOST_SPIRIT_INSTANTIATE
。