将Boost.Spirit.X3解析器拆分为多个TU [英] Splitting Boost.Spirit.X3 parsers into several TUs

查看:73
本文介绍了将Boost.Spirit.X3解析器拆分为多个TU的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我又在和Boost.Spirit.X3挣扎.

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

  • group.hpp -包含用于使用的那些解析器的 typedef s, BOOST_SPIRIT_DECLARE extern 变量声明外面"
  • group_def.hpp -包括上一个,并且包含解析器, BOOST_SPIRIT_DEFINE 等的实际定义.
  • group.cpp -包括上一个,并包含显式模板实例化(通过 BOOST_SPIRIT_INSTANTIATE )

基本上,它或多或少地遵循官方这里 (位于Wandbox上),因为在此处列出所有文件很不方便.虽然它无法正常工作,但由于某些外部问题无法解决,所以很可能我实例化了一些错误的东西,但是我已经花了大约一个星期的时间,所以我很拼命,不要这样做.觉得我能够自己解决这个问题.

一些问题和答案:

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

首先,因为我试图以某种方式进行修改,所以我不想在任何小的更改下都重新编译所有内容(不确定我是否成功完成了此操作),所以其主意是 somegroup.hpp 只是带有声明的轻量"标题, somegroup_def.hpp 是带有定义的标题,而 somegroup.cpp 只是为了创建翻译单元.

第二,我拆分了 _def.hpp .cpp ,因为我还将这些 _def.hpp 文件直接包含在测试中不仅是 extern 解析器,而且还有内部"辅助解析器.

为什么要使用 extern 变量?

我也尝试过使用返回解析器的函数来进行尝试(类似于本教程中的操作).基本上,这就是它的实现方式和现在的工作方式.我不喜欢它,因为例如,给定一个解析器 lang :: parser :: import ,我必须给一个函数起另一个名字( lang :: parser :: import_ )或将其放置在另一个名称空间(即 lang :: import )中.另外,我喜欢直接使用解析器的方式在Spirit本身中的实现方式(即,不带括号: import import _()).

我的实际问题如下:

  • 如果我想将解析器分布在多个翻译单元中,如何正确组织结构?
  • 在上面的代码示例中我到底缺少什么,以致于它不链接?

我将不胜感激.

解决方案

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

我本人发现将 grammar_def.hpp grammar.cpp 分开是没有用的,但这只是一个意见.

为什么要使用外部变量?

我也尝试过使用返回解析器的函数

请勿执行此操作.它将导致静态初始化顺序的惨败.规则占位符足够轻巧,可以在每个翻译单元中实例化它们.

如果我想将解析器分布在多个翻译单元中,如何正确组织结构?

这是关于口味的问题.您所需要做的就是在一个TU中实例化一个规则,并在每一个使用该TU的其他单元中都有一个 x3 :: rule< ...> + BOOST_SPIRIT_DECLARE ./p>

实现此目标的最佳方法是将 x3 :: rule< ...> 分离为 .cpp / _def.hpp 放入单独的标头(将其放入轻量级" .hpp )中,并将其包含在需要这些规则的每个TU中.

请参见 https://github.com/mapnik/mapnik/pull/4072/文件 https://github.com/boostorg/spirit/pull/493/文件

在上面的代码示例中,我确切地缺少什么,以便它不会链接?

  1. 您正在混合 std :: string :: const_iterator std :: string :: iterator 迭代器.
  2. 您正在使用某些跳过程序作为解析器(例如, document_def = eols>> + megarule>> eols ),但不要在适当的上下文中实例化它们.要么根本不让它们成为规则,要么不添加 BOOST_SPIRIT_INSTANTIATE 并添加您在错误消息中看到的上下文.

I'm struggling with Boost.Spirit.X3 again.

I have several logical groups of parsers (statements, expressions, etc.), each of which is represented by several files:

  • group.hpp - contains typedefs, BOOST_SPIRIT_DECLARE and extern variable declaration for those parsers that are used "outside"
  • group_def.hpp - includes the previous one and contains actual definitions of parsers, BOOST_SPIRIT_DEFINE, etc.
  • group.cpp - includes the previous one and contains explicit template instantiations (via BOOST_SPIRIT_INSTANTIATE)

Basically, it more or less follows the structure proposed in the official tutorial. The only difference is that my grammar is much more complicated so I'm trying to split it into several translation units. All these TUs are then compiled into one library which, in turn, is linked then to the main executable.

I've tried to make a "minimal" example here (live on Wandbox) as it would be inconvenient to list all the files here. It doesn't work though because of some unresolved externals, so, most probably, I instantiate something incorrectly, but I've already spent about a week for this, so I'm quite desperate and don't feel like I'm able to handle this on my own.

A few questions and answers:

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

First, because I tried to make it in such a way that I don't want to recompile everything on any small change (not sure if I succeeded in this), so the idea is that somegroup.hpp is just a "lightweight" header with declarations, somegroup_def.hpp is a header with definitions, and somegroup.cpp is just used in order to create a translation unit.

Second, I split _def.hpp and .cpp because I also include these _def.hpp-files directly to tests where I cover not only extern parsers but also "internal" auxiliary ones.

Why am I using extern variables?

I tried it also with functions that return parsers instead (similar to how it is done in the tutorial). Basically, this is the way how it is implemented and does work now. I don't like it because, for instance, given a parser lang::parser::import, I have to either give a function another name (lang::parser::import_) or place it within another namespace (i.e. lang::import). Also, I like the way to use parsers directly how it is done in the Spirit itself (i.e. without parentheses: import vs import_()).

My actual questions are the following:

  • How to properly organize the structure if I want to spread my parsers over several translation units?
  • And what exactly am I missing in the example of code above, so that it doesn't link?

I would appreciate any help.

解决方案

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

I am, myself, find separation into grammar_def.hpp and grammar.cpp useless, but that is just an opinion.

Why am I using extern variables?

I tried it also with functions that return parsers instead

Do not do this. It will lead to static initialization order fiasco. Rule placeholders are lightweight enough to instantiate them in every translation unit.

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

That is a question about tastes. All you need is to instantiate a rule in one of the TU and have a x3::rule<...>+BOOST_SPIRIT_DECLARE in every other that uses it.

The best way to achieve that is to split x3::rule<...> off .cpp/_def.hpp into separate header (place it into your "lightweight" .hpp), and include it in every TU that needs those rules.

See 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. You are mixing std::string::const_iterator and std::string::iterator iterators.
  2. You are using some of your skippers as parsers (f.ex. document_def = eols >> +megarule >> eols) but do not instantiate them with a proper context. Either simply do not make them rules or add BOOST_SPIRIT_INSTANTIATE with a context that you see in the error message.

这篇关于将Boost.Spirit.X3解析器拆分为多个TU的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆