混合来自分离翻译单元的非终端规则
Mixing non-terminal rules from separeted translation unit
简介
我正在尝试使用两个非终端规则,但它们未在同一翻译单元中定义。下面提供了一个重现该问题的最小示例,也可用 live on Coliru
TEST0
直接重用规则(不将其嵌入到另一个规则中)工作正常,尽管它是在另一个翻译单元中定义的。这是 X3 文档中众所周知的 X3 program structure 示例。这是下面实测中的配置TEST0
TEST1
我最初避免将 BOOST_SPIRIT_DEFINE/DECLARE/INSTANTIATE()
宏用于非终端规则之一:
auto const parser2
= x3::rule<class u2,uint64_t>{"parser2"}
= "Trace Address: " >> parser1();
导致 未解析的外部符号 链接器错误 。令人惊讶的是,丢失的罪魁祸首是 parser1
的符号(而不是 parser2
的),为此使用了 BOOST_XXX
宏(参见 unit1.cpp)。这是配置TEST1
TEST2
然后我转到配置 TEST2
,其中为两个规则定义了 BOOST_XXX
宏。此解决方案使用 Visual Studio 2019 (v16.8.3) 编译和运行,但使用 g++ 生成核心转储(如下面的测试所示)。
重现问题的最小示例
unit1.h
#ifndef UNIT1_H
#define UNIT1_H
#include <cstdint>
#include "boost/spirit/home/x3.hpp"
#include "boost/spirit/include/support_istream_iterator.hpp"
namespace x3 = boost::spirit::x3;
using iter_t = boost::spirit::istream_iterator;
using context_t = x3::phrase_parse_context<x3::ascii::space_type>::type;
namespace unit1 {
using parser1_t = x3::rule<class u1, std::uint64_t>;
BOOST_SPIRIT_DECLARE(parser1_t);
}
unit1::parser1_t const& parser1();
#endif /* UNIT1_H */
unit1.cpp
#include "unit1.h"
namespace unit1 {
parser1_t const parser1 = "unit1_rule";
auto const parser1_def = x3::uint_;
BOOST_SPIRIT_DEFINE(parser1)
BOOST_SPIRIT_INSTANTIATE(parser1_t, iter_t, context_t)
}
unit1::parser1_t const& parser1() { return unit1::parser1; }
main.cpp
#include <iostream>
#include "unit1.h"
namespace x3 = boost::spirit::x3;
#define TEST2
#ifdef TEST2
auto const parser2 = x3::rule<class u2, uint64_t>{"parser2"};
auto const parser2_def = "Trace address: " >> parser1();
BOOST_SPIRIT_DECLARE(decltype(parser2))
BOOST_SPIRIT_DEFINE(parser2)
BOOST_SPIRIT_INSTANTIATE(decltype(parser2),iter_t,context_t)
#endif
int main(int argc, char* argv[])
{
std::string input("Trace address: 123434");
std::istringstream i(input);
std::cout << "parsing: " << input << "\n";
boost::spirit::istream_iterator b{i >> std::noskipws};
boost::spirit::istream_iterator e{};
uint64_t addr=0;
#ifdef TEST0
bool v = x3::phrase_parse(b, e, "Trace address: " >> parser1(), x3::ascii::space,addr);
#elif defined TEST1
auto const parser2
= x3::rule<class u2, uint64_t>{ "parser2" }
= "Trace address: " >> parser1();
bool v = x3::phrase_parse(b, e, parser2, x3::ascii::space,addr);
#elif defined TEST2
bool v = x3::phrase_parse(b, e, parser2, x3::ascii::space,addr);
#endif
std::cout << "result: " << (v ? "OK" : "Failed") << "\n";
std::cout << "result: " << addr << "\n";
return v;
}
我觉得我没有正确地做这些事情,这是我的问题:
未解析的外部符号和解析器上下文
在配置 TEST1
中,错误消息是 undefined reference to unit1::parse_rule<...>
这意味着 parser1
未使用正确的上下文实例化。好的,但是在这种情况下我应该使用什么上下文呢?即使我将 parser2
移出 main()
函数,我也会或多或少地遇到同样的问题。我当然可以显示上下文,并尝试使用它 BOOST_SPIRIT_INSTANTIATE()
但我觉得这不是要走的路。令人惊讶的是,它似乎实例化了 parser2
,解决了问题(至少在 Visual Studio 上)
来自分离翻译单元的混合规则
为什么这么复杂,而如果我删除 parser2
中的规则,一切正常?
Q. Why is it so complicated [...]
通过标记类型 (rule-id) 对规则进行静态 link 规则定义的机制很棘手。它实际上取决于 parse_rule¹ 函数模板的专门化。
但是函数模板依赖于:
- 规则 ID(“标签类型”)
- 迭代器类型
- 上下文(包括船长或
with<>
指令)
所有类型必须完全匹配。这是一个常见的错误来源。
Q. [...] whereas if I remove the rule in parser2, every thing works ok ?
可能是因为规则定义对编译器可见以在此时实例化,或者因为类型与刚才描述的匹配。
稍后我会查看您的具体代码。
重现
- 测试 0 https://wandbox.org/permlink/ElHfvW343nvqiT2p
- 测试 1 https://wandbox.org/permlink/37NgtQvXeAwwdoU6 - 问题
- 测试 2 https://wandbox.org/permlink/HwhqI5v7FEf0I2I7
阅读编译器消息
我的编译器用 -DTEST1:
发出警告
unit1.h|13 col 5| warning: ‘bool unit1::parse_rule(unit1::parser1_t, Iterator&, const Iterator&, const Context&, boost::spirit::x3::rule<unit1::u1, long unsigned int>::attribute_type&) [with Iterator = boost::spirit::basic_istream_iterator<char>; Context = boost::spirit::x3::context<main()::u2, const boost::spirit::x3::sequence<boost::spirit::x3::literal_string<const char*, boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type>, boost::spirit::x3::rule<unit1::u1, long unsigned int> >, boost::spirit::x3::context<boost::spirit::x3::skipper_tag, const boost::spirit::x3::char_class<boost::spirit::char_encoding::ascii, boost::spirit::x3::space_tag>, boost::spirit::x3::unused_type> >]’ used but never defined
这将模板特化的 精确 类型参数拼写为 TU 中的 explicitly-instantiate。
linker 错误拼写了缺失的符号:
/home/sehe/custom/spirit/include/boost/spirit/home/x3/nonterminal/rule.hpp:135: undefined reference to
bool
unit1::parse_rule<boost::spirit::basic_istream_iterator<char,
std::char_traits >, boost::spirit::x3::context<main::u2,
boost::spirit::x3::sequence<boost::spirit::x3::literal_string<char
const*, boost::spirit::char_encoding::standard,
boost::spirit::x3::unused_type>, boost::spirit::x3::rule<unit1::u1,
unsigned long, false> > const,
boost::spirit::x3::context<boost::spirit::x3::skipper_tag,
boost::spirit::x3::char_class<boost::spirit::char_encoding::ascii,
boost::spirit::x3::space_tag> const, boost::spirit::x3::unused_type> >
(boost::spirit::x3::rule<unit1::u1, unsigned long, false>, boost::spirit::basic_istream_iterator<char, std::char_traits >&,
boost::spirit::basic_istream_iterator<char, std::char_traits >
const&, boost::spirit::x3::context<main::u2,
boost::spirit::x3::sequence<boost::spirit::x3::literal_string<char
const*, boost::spirit::char_encoding::standard,
boost::spirit::x3::unused_type>, boost::spirit::x3::rule<unit1::u1,
unsigned long, false> > const,
boost::spirit::x3::context<boost::spirit::x3::skipper_tag,
boost::spirit::x3::char_class<boost::spirit::char_encoding::ascii,
boost::spirit::x3::space_tag> const, boost::spirit::x3::unused_type> >
const&, unsigned long&)'`
总而言之,您的任务就是比较它们 (!!) 并注意差异。
阅读宏魔法
扩展宏得到
template <typename Iterator, typename Context> inline bool parse_rule( decltype(parser1) , Iterator& first, Iterator const& last , Context const& context, decltype(parser1)::attribute_type& attr) { using boost::spirit::x3::unused; static auto const def_ = (parser1 = parser1_def); return def_.parse(first, last, context, unused, attr); }
template bool parse_rule<iter_t, context_t>( parser1_t rule_ , iter_t& first, iter_t const& last , context_t const& context, parser1_t::attribute_type&);
这是给...定义的:
template <typename Iterator, typename Context>
inline bool parse_rule(decltype(parser1), Iterator& first,
Iterator const& last, Context const& context,
decltype(parser1)::attribute_type& attr)
{
using boost::spirit::x3::unused;
static auto const def_ = (parser1 = parser1_def);
return def_.parse(first, last, context, unused, attr);
}
并且对于显式...实例化:
template bool parse_rule<iter_t, context_t>(parser1_t rule_, iter_t& first,
iter_t const& last, context_t const& context,
parser1_t::attribute_type&);
替换类型显示实例化的内容(见上面的警告)。
其他选项
不用费力,我们知道哪些模板类型参数可能是错误的,所以让我们检查一下:
迭代器:
static_assert(std::is_same_v<iter_t, boost::spirit::istream_iterator>);
iter_t b{i >> std::noskipws}, e {};
这不是罪魁祸首,编译器确认。
船长应该是x3::ascii::space_type
,这似乎也很匹配。
问题一定出在语境上。现在让我们从 linker 错误中提取上下文:
bool unit1::parse_rule<...> >
(x3::rule<unit1::u1, unsigned long, false>, iter_t &, iter_t const &,
// this is the context:
x3::context<
main::u2,
x3::sequence<x3::literal_string<char const *,
boost::spirit::char_encoding::standard,
x3::unused_type>,
x3::rule<unit1::u1, unsigned long, false>> const,
x3::context<x3::skipper_tag,
x3::char_class<boost::spirit::char_encoding::ascii,
x3::space_tag> const,
x3::unused_type>> const &,
// this is the attribtue
unsigned long &);
上下文看起来并不像我们所期望的那样。我认为问题在于 rule2 定义“可见”导致包含定义的上下文(这是允许本地 x3::rule 定义而根本没有定义宏魔法的机制)。
I remember a more recent mailing list post pointing this out (and it was kind of a surprise to me back then):
https://sourceforge.net/p/spirit/mailman/message/37194823/
关于 di,05.jan 13:12,Larry Evans 写道:
However, there's another reason to use BOOST_SPIRIT_DEFINE. When
there is a lot of recursive rules, and BOOST_SPIRIT_DEFINE is not
used, this causes much heavier template processing and concomitant
slow compile times. The reason is that, without BOOST_SPIRIT_DEFINE,
the definition for a rule is stored in the context and this is what
causes the explosion in compile-times.
So, be aware of this when you notice compile times slow as you add
more recursive rules.
感谢您指出这一点。我已经 运行 在没有意识到的情况下
省略定义分隔是一个关键因素。
我想在某些情况下它也可以提供缓解
当规则改变船长时导致极端模板递归
(因为上下文在技术上一直不同)。
同样,这实际上是一个非常有用的注释。谢谢
赛斯
在帖子的前面,我表达了我不喜欢宏机制的原因,并且 从不 将我的 X3 规则传播到 TU。到现在为止,您可能会欣赏这种情绪:)
解决方法
您可以通过制造正确的上下文类型并将其实例化(以及)来解决此问题:(unit1.h)
struct u2;
using context2_t = x3::context<
u2,
decltype("" >> parser1_t{}) const,
context_t>;
BOOST_SPIRIT_DECLARE(parser1_t)
并且在 cpp 中:
BOOST_SPIRIT_DEFINE(parser1)
BOOST_SPIRIT_INSTANTIATE(parser1_t, iter_t, context_t) // optionally
BOOST_SPIRIT_INSTANTIATE(parser1_t, iter_t, context2_t)
不足为奇,这有效:https://wandbox.org/permlink/Y6NsKCcIDgiHGJf2
总结
令我惊讶的是,我又一次找到了不喜欢 X3 规则分离魔法的理由。但是,如果您需要它,您可能不应该混合搭配,但也可以定义 parser2
out-of-line。
namespace unit2 {
parser2_t parser2 = "unit2_rule";
auto const parser2_def = "Trace address: " >> parser1();
BOOST_SPIRIT_DEFINE(parser2)
BOOST_SPIRIT_INSTANTIATE(parser2_t, iter_t, context_t)
} // namespace unit2
完整列表
致Wandbox的后代:
文件unit1.cpp
#include "unit1.h"
namespace unit1 {
parser1_t parser1 = "unit1_rule";
auto const parser1_def = x3::uint_;
BOOST_SPIRIT_DEFINE(parser1)
BOOST_SPIRIT_INSTANTIATE(parser1_t, iter_t, context_t)
} // namespace unit1
unit1::parser1_t const &parser1() { return unit1::parser1; }
文件unit1.h
#ifndef UNIT1_H
#define UNIT1_H
#include "boost/spirit/home/x3.hpp"
#include "boost/spirit/include/support_istream_iterator.hpp"
#include <cstdint>
namespace x3 = boost::spirit::x3;
using iter_t = boost::spirit::istream_iterator;
using context_t = x3::phrase_parse_context<x3::ascii::space_type>::type;
namespace unit1 {
using parser1_t = x3::rule<class u1, std::uint64_t> const;
BOOST_SPIRIT_DECLARE(parser1_t)
} // namespace unit1
unit1::parser1_t const &parser1();
#endif /* UNIT1_H */
文件unit2.cpp
#include "unit2.h"
#include "unit1.h"
namespace unit2 {
parser2_t parser2 = "unit2_rule";
auto const parser2_def = "Trace address: " >> parser1();
BOOST_SPIRIT_DEFINE(parser2)
BOOST_SPIRIT_INSTANTIATE(parser2_t, iter_t, context_t)
} // namespace unit2
unit2::parser2_t const &parser2() { return unit2::parser2; }
文件unit2.h
#ifndef UNIT2_H
#define UNIT2_H
#include "boost/spirit/home/x3.hpp"
#include "boost/spirit/include/support_istream_iterator.hpp"
#include <cstdint>
namespace x3 = boost::spirit::x3;
using iter_t = boost::spirit::istream_iterator;
using context_t = x3::phrase_parse_context<x3::ascii::space_type>::type;
namespace unit2 {
using parser2_t = x3::rule<class u2, std::uint64_t> const;
BOOST_SPIRIT_DECLARE(parser2_t)
} // namespace unit2
unit2::parser2_t const &parser2();
#endif /* UNIT2_H */
文件main.cpp
#include "unit2.h"
#include <iostream>
namespace x3 = boost::spirit::x3;
int main() {
std::string input("Trace address: 123434");
std::istringstream i(input);
std::cout << "parsing: " << input << "\n";
static_assert(std::is_same_v<iter_t, boost::spirit::istream_iterator>);
iter_t b{i >> std::noskipws}, e {};
uint64_t addr = 0;
bool v = x3::phrase_parse(b, e, parser2(), x3::ascii::space, addr);
std::cout << "result: " << (v ? "OK" : "Failed") << "\n";
std::cout << "result: " << addr << "\n";
return v;
}
简介
我正在尝试使用两个非终端规则,但它们未在同一翻译单元中定义。下面提供了一个重现该问题的最小示例,也可用 live on Coliru
TEST0
直接重用规则(不将其嵌入到另一个规则中)工作正常,尽管它是在另一个翻译单元中定义的。这是 X3 文档中众所周知的 X3 program structure 示例。这是下面实测中的配置TEST0
TEST1
我最初避免将 BOOST_SPIRIT_DEFINE/DECLARE/INSTANTIATE()
宏用于非终端规则之一:
auto const parser2
= x3::rule<class u2,uint64_t>{"parser2"}
= "Trace Address: " >> parser1();
导致 未解析的外部符号 链接器错误 。令人惊讶的是,丢失的罪魁祸首是 parser1
的符号(而不是 parser2
的),为此使用了 BOOST_XXX
宏(参见 unit1.cpp)。这是配置TEST1
TEST2
然后我转到配置 TEST2
,其中为两个规则定义了 BOOST_XXX
宏。此解决方案使用 Visual Studio 2019 (v16.8.3) 编译和运行,但使用 g++ 生成核心转储(如下面的测试所示)。
重现问题的最小示例
unit1.h
#ifndef UNIT1_H
#define UNIT1_H
#include <cstdint>
#include "boost/spirit/home/x3.hpp"
#include "boost/spirit/include/support_istream_iterator.hpp"
namespace x3 = boost::spirit::x3;
using iter_t = boost::spirit::istream_iterator;
using context_t = x3::phrase_parse_context<x3::ascii::space_type>::type;
namespace unit1 {
using parser1_t = x3::rule<class u1, std::uint64_t>;
BOOST_SPIRIT_DECLARE(parser1_t);
}
unit1::parser1_t const& parser1();
#endif /* UNIT1_H */
unit1.cpp
#include "unit1.h"
namespace unit1 {
parser1_t const parser1 = "unit1_rule";
auto const parser1_def = x3::uint_;
BOOST_SPIRIT_DEFINE(parser1)
BOOST_SPIRIT_INSTANTIATE(parser1_t, iter_t, context_t)
}
unit1::parser1_t const& parser1() { return unit1::parser1; }
main.cpp
#include <iostream>
#include "unit1.h"
namespace x3 = boost::spirit::x3;
#define TEST2
#ifdef TEST2
auto const parser2 = x3::rule<class u2, uint64_t>{"parser2"};
auto const parser2_def = "Trace address: " >> parser1();
BOOST_SPIRIT_DECLARE(decltype(parser2))
BOOST_SPIRIT_DEFINE(parser2)
BOOST_SPIRIT_INSTANTIATE(decltype(parser2),iter_t,context_t)
#endif
int main(int argc, char* argv[])
{
std::string input("Trace address: 123434");
std::istringstream i(input);
std::cout << "parsing: " << input << "\n";
boost::spirit::istream_iterator b{i >> std::noskipws};
boost::spirit::istream_iterator e{};
uint64_t addr=0;
#ifdef TEST0
bool v = x3::phrase_parse(b, e, "Trace address: " >> parser1(), x3::ascii::space,addr);
#elif defined TEST1
auto const parser2
= x3::rule<class u2, uint64_t>{ "parser2" }
= "Trace address: " >> parser1();
bool v = x3::phrase_parse(b, e, parser2, x3::ascii::space,addr);
#elif defined TEST2
bool v = x3::phrase_parse(b, e, parser2, x3::ascii::space,addr);
#endif
std::cout << "result: " << (v ? "OK" : "Failed") << "\n";
std::cout << "result: " << addr << "\n";
return v;
}
我觉得我没有正确地做这些事情,这是我的问题:
未解析的外部符号和解析器上下文
在配置 TEST1
中,错误消息是 undefined reference to unit1::parse_rule<...>
这意味着 parser1
未使用正确的上下文实例化。好的,但是在这种情况下我应该使用什么上下文呢?即使我将 parser2
移出 main()
函数,我也会或多或少地遇到同样的问题。我当然可以显示上下文,并尝试使用它 BOOST_SPIRIT_INSTANTIATE()
但我觉得这不是要走的路。令人惊讶的是,它似乎实例化了 parser2
,解决了问题(至少在 Visual Studio 上)
来自分离翻译单元的混合规则
为什么这么复杂,而如果我删除 parser2
中的规则,一切正常?
Q. Why is it so complicated [...]
通过标记类型 (rule-id) 对规则进行静态 link 规则定义的机制很棘手。它实际上取决于 parse_rule¹ 函数模板的专门化。
但是函数模板依赖于:
- 规则 ID(“标签类型”)
- 迭代器类型
- 上下文(包括船长或
with<>
指令)
所有类型必须完全匹配。这是一个常见的错误来源。
Q. [...] whereas if I remove the rule in parser2, every thing works ok ?
可能是因为规则定义对编译器可见以在此时实例化,或者因为类型与刚才描述的匹配。
稍后我会查看您的具体代码。
重现
- 测试 0 https://wandbox.org/permlink/ElHfvW343nvqiT2p
- 测试 1 https://wandbox.org/permlink/37NgtQvXeAwwdoU6 - 问题
- 测试 2 https://wandbox.org/permlink/HwhqI5v7FEf0I2I7
阅读编译器消息
我的编译器用 -DTEST1:
发出警告
unit1.h|13 col 5| warning: ‘bool unit1::parse_rule(unit1::parser1_t, Iterator&, const Iterator&, const Context&, boost::spirit::x3::rule<unit1::u1, long unsigned int>::attribute_type&) [with Iterator = boost::spirit::basic_istream_iterator<char>; Context = boost::spirit::x3::context<main()::u2, const boost::spirit::x3::sequence<boost::spirit::x3::literal_string<const char*, boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type>, boost::spirit::x3::rule<unit1::u1, long unsigned int> >, boost::spirit::x3::context<boost::spirit::x3::skipper_tag, const boost::spirit::x3::char_class<boost::spirit::char_encoding::ascii, boost::spirit::x3::space_tag>, boost::spirit::x3::unused_type> >]’ used but never defined
这将模板特化的 精确 类型参数拼写为 TU 中的 explicitly-instantiate。
linker 错误拼写了缺失的符号:
/home/sehe/custom/spirit/include/boost/spirit/home/x3/nonterminal/rule.hpp:135: undefined reference to
bool unit1::parse_rule<boost::spirit::basic_istream_iterator<char, std::char_traits >, boost::spirit::x3::context<main::u2, boost::spirit::x3::sequence<boost::spirit::x3::literal_string<char const*, boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type>, boost::spirit::x3::rule<unit1::u1, unsigned long, false> > const, boost::spirit::x3::context<boost::spirit::x3::skipper_tag, boost::spirit::x3::char_class<boost::spirit::char_encoding::ascii, boost::spirit::x3::space_tag> const, boost::spirit::x3::unused_type> >(boost::spirit::x3::rule<unit1::u1, unsigned long, false>, boost::spirit::basic_istream_iterator<char, std::char_traits >&, boost::spirit::basic_istream_iterator<char, std::char_traits > const&, boost::spirit::x3::context<main::u2, boost::spirit::x3::sequence<boost::spirit::x3::literal_string<char const*, boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type>, boost::spirit::x3::rule<unit1::u1, unsigned long, false> > const, boost::spirit::x3::context<boost::spirit::x3::skipper_tag, boost::spirit::x3::char_class<boost::spirit::char_encoding::ascii, boost::spirit::x3::space_tag> const, boost::spirit::x3::unused_type> > const&, unsigned long&)'`
总而言之,您的任务就是比较它们 (!!) 并注意差异。
阅读宏魔法
扩展宏得到
template <typename Iterator, typename Context> inline bool parse_rule( decltype(parser1) , Iterator& first, Iterator const& last , Context const& context, decltype(parser1)::attribute_type& attr) { using boost::spirit::x3::unused; static auto const def_ = (parser1 = parser1_def); return def_.parse(first, last, context, unused, attr); }
template bool parse_rule<iter_t, context_t>( parser1_t rule_ , iter_t& first, iter_t const& last , context_t const& context, parser1_t::attribute_type&);
这是给...定义的:
template <typename Iterator, typename Context>
inline bool parse_rule(decltype(parser1), Iterator& first,
Iterator const& last, Context const& context,
decltype(parser1)::attribute_type& attr)
{
using boost::spirit::x3::unused;
static auto const def_ = (parser1 = parser1_def);
return def_.parse(first, last, context, unused, attr);
}
并且对于显式...实例化:
template bool parse_rule<iter_t, context_t>(parser1_t rule_, iter_t& first,
iter_t const& last, context_t const& context,
parser1_t::attribute_type&);
替换类型显示实例化的内容(见上面的警告)。
其他选项
不用费力,我们知道哪些模板类型参数可能是错误的,所以让我们检查一下:
迭代器:
static_assert(std::is_same_v<iter_t, boost::spirit::istream_iterator>); iter_t b{i >> std::noskipws}, e {};
这不是罪魁祸首,编译器确认。
船长应该是
x3::ascii::space_type
,这似乎也很匹配。问题一定出在语境上。现在让我们从 linker 错误中提取上下文:
bool unit1::parse_rule<...> > (x3::rule<unit1::u1, unsigned long, false>, iter_t &, iter_t const &, // this is the context: x3::context< main::u2, x3::sequence<x3::literal_string<char const *, boost::spirit::char_encoding::standard, x3::unused_type>, x3::rule<unit1::u1, unsigned long, false>> const, x3::context<x3::skipper_tag, x3::char_class<boost::spirit::char_encoding::ascii, x3::space_tag> const, x3::unused_type>> const &, // this is the attribtue unsigned long &);
上下文看起来并不像我们所期望的那样。我认为问题在于 rule2 定义“可见”导致包含定义的上下文(这是允许本地 x3::rule 定义而根本没有定义宏魔法的机制)。
I remember a more recent mailing list post pointing this out (and it was kind of a surprise to me back then): https://sourceforge.net/p/spirit/mailman/message/37194823/
关于 di,05.jan 13:12,Larry Evans 写道:
However, there's another reason to use BOOST_SPIRIT_DEFINE. When there is a lot of recursive rules, and BOOST_SPIRIT_DEFINE is not used, this causes much heavier template processing and concomitant slow compile times. The reason is that, without BOOST_SPIRIT_DEFINE, the definition for a rule is stored in the context and this is what causes the explosion in compile-times.
So, be aware of this when you notice compile times slow as you add more recursive rules.
感谢您指出这一点。我已经 运行 在没有意识到的情况下 省略定义分隔是一个关键因素。
我想在某些情况下它也可以提供缓解 当规则改变船长时导致极端模板递归 (因为上下文在技术上一直不同)。
同样,这实际上是一个非常有用的注释。谢谢
赛斯
在帖子的前面,我表达了我不喜欢宏机制的原因,并且 从不 将我的 X3 规则传播到 TU。到现在为止,您可能会欣赏这种情绪:)
解决方法
您可以通过制造正确的上下文类型并将其实例化(以及)来解决此问题:(unit1.h)
struct u2;
using context2_t = x3::context<
u2,
decltype("" >> parser1_t{}) const,
context_t>;
BOOST_SPIRIT_DECLARE(parser1_t)
并且在 cpp 中:
BOOST_SPIRIT_DEFINE(parser1)
BOOST_SPIRIT_INSTANTIATE(parser1_t, iter_t, context_t) // optionally
BOOST_SPIRIT_INSTANTIATE(parser1_t, iter_t, context2_t)
不足为奇,这有效:https://wandbox.org/permlink/Y6NsKCcIDgiHGJf2
总结
令我惊讶的是,我又一次找到了不喜欢 X3 规则分离魔法的理由。但是,如果您需要它,您可能不应该混合搭配,但也可以定义 parser2
out-of-line。
namespace unit2 {
parser2_t parser2 = "unit2_rule";
auto const parser2_def = "Trace address: " >> parser1();
BOOST_SPIRIT_DEFINE(parser2)
BOOST_SPIRIT_INSTANTIATE(parser2_t, iter_t, context_t)
} // namespace unit2
完整列表
致Wandbox的后代:
文件
unit1.cpp
#include "unit1.h" namespace unit1 { parser1_t parser1 = "unit1_rule"; auto const parser1_def = x3::uint_; BOOST_SPIRIT_DEFINE(parser1) BOOST_SPIRIT_INSTANTIATE(parser1_t, iter_t, context_t) } // namespace unit1 unit1::parser1_t const &parser1() { return unit1::parser1; }
文件
unit1.h
#ifndef UNIT1_H #define UNIT1_H #include "boost/spirit/home/x3.hpp" #include "boost/spirit/include/support_istream_iterator.hpp" #include <cstdint> namespace x3 = boost::spirit::x3; using iter_t = boost::spirit::istream_iterator; using context_t = x3::phrase_parse_context<x3::ascii::space_type>::type; namespace unit1 { using parser1_t = x3::rule<class u1, std::uint64_t> const; BOOST_SPIRIT_DECLARE(parser1_t) } // namespace unit1 unit1::parser1_t const &parser1(); #endif /* UNIT1_H */
文件
unit2.cpp
#include "unit2.h" #include "unit1.h" namespace unit2 { parser2_t parser2 = "unit2_rule"; auto const parser2_def = "Trace address: " >> parser1(); BOOST_SPIRIT_DEFINE(parser2) BOOST_SPIRIT_INSTANTIATE(parser2_t, iter_t, context_t) } // namespace unit2 unit2::parser2_t const &parser2() { return unit2::parser2; }
文件
unit2.h
#ifndef UNIT2_H #define UNIT2_H #include "boost/spirit/home/x3.hpp" #include "boost/spirit/include/support_istream_iterator.hpp" #include <cstdint> namespace x3 = boost::spirit::x3; using iter_t = boost::spirit::istream_iterator; using context_t = x3::phrase_parse_context<x3::ascii::space_type>::type; namespace unit2 { using parser2_t = x3::rule<class u2, std::uint64_t> const; BOOST_SPIRIT_DECLARE(parser2_t) } // namespace unit2 unit2::parser2_t const &parser2(); #endif /* UNIT2_H */
文件
main.cpp
#include "unit2.h" #include <iostream> namespace x3 = boost::spirit::x3; int main() { std::string input("Trace address: 123434"); std::istringstream i(input); std::cout << "parsing: " << input << "\n"; static_assert(std::is_same_v<iter_t, boost::spirit::istream_iterator>); iter_t b{i >> std::noskipws}, e {}; uint64_t addr = 0; bool v = x3::phrase_parse(b, e, parser2(), x3::ascii::space, addr); std::cout << "result: " << (v ? "OK" : "Failed") << "\n"; std::cout << "result: " << addr << "\n"; return v; }