C++ 从分离翻译单元混合非终结规则 介绍

C++ 从分离翻译单元混合非终结规则 介绍,c++,boost,boost-spirit,boost-spirit-x3,C++,Boost,Boost Spirit,Boost Spirit X3,我试图使用两个非终端规则,而它们不是在同一个翻译单元中定义的。下面提供了再现该问题的最简单示例,也可从 TEST0 直接重新使用一个规则(不将其嵌入另一个规则)可以正常工作,尽管它是在另一个翻译单元中定义的。这是X3文档中众所周知的示例。这是下面实时测试中的配置TEST0 TEST1 我最初避免使用BOOST\u SPIRIT\u DEFINE/DECLARE/INSTANTIATE()宏作为一个非终端规则: auto const parser2 = x3::rule<class

我试图使用两个非终端规则,而它们不是在同一个翻译单元中定义的。下面提供了再现该问题的最简单示例,也可从

TEST0

直接重新使用一个规则(不将其嵌入另一个规则)可以正常工作,尽管它是在另一个翻译单元中定义的。这是X3文档中众所周知的示例。这是下面实时测试中的配置
TEST0

TEST1

我最初避免使用
BOOST\u SPIRIT\u DEFINE/DECLARE/INSTANTIATE()
宏作为一个非终端规则:

auto const parser2 
    = x3::rule<class u2,uint64_t>{"parser2"} 
    = "Trace Address: " >> parser1();
#包括
#包括“unit1.h”
名称空间x3=boost::spirit::x3;
#定义TEST2
#ifdef测试2
auto const parser2=x3::规则{“parser2”};
auto const parser2_def=“跟踪地址:”>>parser1();
增强精神声明(decltype(parser2))
提升精神定义(解析器2)
BOOST_SPIRIT_实例化(decltype(parser2)、iter_t、context_t)
#恩迪夫
int main(int argc,char*argv[])
{
字符串输入(“跟踪地址:123434”);
std::istringstream i(输入);
std::cout>parser1(),x3::ascii::space,addr);
#elif定义的测试1
自动常量分析器2
=x3::规则{“parser2”}
=“跟踪地址:”>>parser1();
boolv=x3::短语解析(b,e,parser2,x3::ascii::space,addr);
#elif定义的测试2
boolv=x3::短语解析(b,e,parser2,x3::ascii::space,addr);
#恩迪夫
标准::cout
Q.为什么这么复杂[…]

通过标记类型(规则id)将规则定义静态链接到规则的机制很复杂。事实上,这取决于parse_ruleª函数模板的专门化

但是,函数模板取决于:

  • 规则id(“标记类型”)
  • 迭代器类型
  • 上下文(包括skipper或带有指令的
所有类型必须完全匹配。这是一个常见的错误源

Q.[…]然而,如果我在parser2中删除规则,一切正常吗

可能是因为规则定义对编译器在该点上实例化是可见的,或者是因为类型与刚才描述的匹配

我将很快查看您的特定代码

复制
  • 测试0
  • 测试1-问题
  • 测试2
读取编译器消息 我的编译器使用-DTEST1发出警告:

unit1.h | 13 col 5 |警告:“bool unit1::parse_rule(unit1::parser1_t,迭代器&,常量迭代器&,常量迭代器&,常量上下文&,boost::spirit::x3::rule::attribute_type&[with Iterator=boost::spirit::basic_istream_迭代器;Context=boost::spirit::x3::Context]”使用但从未定义过

这会将TU中模板专门化的确切的类型参数拼写为

链接器错误拼写缺少的符号:

/home/sehe/custom/spirit/include/boost/spirit/home/x3/nonterminal/rule.hpp:135:未定义对
bool的引用
unit1::parse_Rule以两种方式解决了这个问题。并从这里再次确认了我的立场:感谢您为调试此类问题提供了方法,这对这个库来说非常有价值。我现在需要时间来承认这一切(也让我的眼睛休息一下!)。我理解,从visual studio迁移到其他编译器可能有助于读取这些看起来更具可读性的错误消息。不过,对于非专家来说,要走的路并不明显,将单独翻译单元中定义的解析器嵌入规则是一个容易/常见的陷阱。在这种情况下:(注意我如何将跳过的语法嵌入规则)我已经提交了一份文件,样本组织得很好。谢谢。
#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 */
#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; }
#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;
}
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 {};
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 &);
struct u2;
using context2_t = x3::context<
    u2,
    decltype("" >> parser1_t{}) const,
    context_t>;

BOOST_SPIRIT_DECLARE(parser1_t)
BOOST_SPIRIT_DEFINE(parser1)
BOOST_SPIRIT_INSTANTIATE(parser1_t, iter_t, context_t) // optionally
BOOST_SPIRIT_INSTANTIATE(parser1_t, iter_t, context2_t)
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
 #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; }
 #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 */
 #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; }
 #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 */
 #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;
 }