将 Boost.Spirit.X3 解析器拆分为多个 TU
Splitting Boost.Spirit.X3 parsers into several TUs
我再次与 Boost.Spirit.X3 作斗争。
我有几个解析器逻辑组(语句、表达式等),每个解析器由几个文件表示:
group.hpp
- 包含 typedef
s、BOOST_SPIRIT_DECLARE
和 extern
变量声明,用于那些使用 "outside" 的解析器
group_def.hpp
- 包括前一个并包含解析器的实际定义,BOOST_SPIRIT_DEFINE
,等等
group.cpp
- 包括前一个并包含显式模板实例化(通过 BOOST_SPIRIT_INSTANTIATE
)
基本上,它或多或少遵循了官方提出的结构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 本身中完成的(即没有括号:import
与 import_()
)。
我的实际问题如下:
- 如果我想将解析器分布在多个翻译单元上,如何正确组织结构?
- 我在上面的代码示例中到底遗漏了什么,所以它没有 link?
如有任何帮助,我将不胜感激。
Why do I prefer using three files per "group"?
我自己觉得分成 grammar_def.hpp
和 grammar.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?
- 您正在混合使用
std::string::const_iterator
和 std::string::iterator
迭代器。
- 您正在使用一些船长作为解析器 (f.ex。
document_def = eols >> +megarule >> eols
),但没有使用适当的上下文实例化它们。要么简单地不让它们成为规则,要么添加 BOOST_SPIRIT_INSTANTIATE
以及您在错误消息中看到的上下文。
我再次与 Boost.Spirit.X3 作斗争。
我有几个解析器逻辑组(语句、表达式等),每个解析器由几个文件表示:
group.hpp
- 包含typedef
s、BOOST_SPIRIT_DECLARE
和extern
变量声明,用于那些使用 "outside" 的解析器
group_def.hpp
- 包括前一个并包含解析器的实际定义,BOOST_SPIRIT_DEFINE
,等等group.cpp
- 包括前一个并包含显式模板实例化(通过BOOST_SPIRIT_INSTANTIATE
)
基本上,它或多或少遵循了官方提出的结构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 本身中完成的(即没有括号:import
与 import_()
)。
我的实际问题如下:
- 如果我想将解析器分布在多个翻译单元上,如何正确组织结构?
- 我在上面的代码示例中到底遗漏了什么,所以它没有 link?
如有任何帮助,我将不胜感激。
Why do I prefer using three files per "group"?
我自己觉得分成 grammar_def.hpp
和 grammar.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?
- 您正在混合使用
std::string::const_iterator
和std::string::iterator
迭代器。 - 您正在使用一些船长作为解析器 (f.ex。
document_def = eols >> +megarule >> eols
),但没有使用适当的上下文实例化它们。要么简单地不让它们成为规则,要么添加BOOST_SPIRIT_INSTANTIATE
以及您在错误消息中看到的上下文。