将一个解析器从一个单独的翻译单元嵌入到另一个解析器中

Embedding a parser from a separate translation unit into another parser

我正在尝试将我在翻译单元 (unit1.h/unit1.cpp) 中隔离的解析器(名为 parser1_rule)重用到另一个解析器(名称 trace_parser ).然而,我收到一条警告和一条错误消息(为了便于阅读,在下面复制和重新格式化)或多或少(这是我的解释)说明 parser1_rule 尚未为 new 定义(或实例化) 上下文 trace_context_t.

的确,在X3中,规则有两个模板参数:迭代器和上下文。我想当混合两个规则时,必须确保它们都使用相同的上下文和迭代器的类型。所以我注意为 parser1_ruletrace_parser 使用相同的上下文:context_type。迭代器也是如此,如

中所述

但它接缝这不是要走的路吗?我可以将规则的定义移动到一个 hpp 文件中吗(我希望我可以避免)。

我创建了一个最小的可重现示例来帮助理解问题:

编辑: 提升 1.71.0 和 Visual Studio 16.8.2

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;
using parser1_t   = x3::rule<class p1, std::uint64_t>;

BOOST_SPIRIT_DECLARE(parser1_t);
parser1_t const & ATparser();

#endif /* UNIT1_H */

unit1.cpp

#include "unit1.h"

parser1_t const parser1 = "parser1_rule";
auto const parser1_def = x3::lexeme[x3::lit("AT") >> x3::uint_];
BOOST_SPIRIT_DEFINE(parser1);
BOOST_SPIRIT_INSTANTIATE(parser1_t, iter_t, context_t);

parser1_t const& ATparser() { return parser1; }

Source.h

#ifndef SOURCE_H
#define SOURCE_H
#include <cstdint>
#include "boost/spirit/home/x3.hpp"
#include "boost/spirit/include/support_istream_iterator.hpp"

namespace x3 = boost::spirit::x3;

namespace trace {
    using trace_parser_tag = struct trace_parser;
    using rule_type = x3::rule<trace_parser_tag>;
    BOOST_SPIRIT_DECLARE(rule_type);
}
trace::rule_type const& trace_parser();
#endif

Source.cpp

#include <iostream>
#include <fstream>
#include "unit1.h"
#include "Source.h"

namespace x3 = boost::spirit::x3;

/** reading:
    Trace address: AT123434 */
namespace trace {
    rule_type const tr = "trace_parser";
    auto fill = [](auto& ctx) {};  /* semantic action to break automatic attribute propagation (first step) */
    auto const tr_def = ("Trace address: " >> ATparser())[fill];
    BOOST_SPIRIT_DEFINE(tr);
    BOOST_SPIRIT_INSTANTIATE(rule_type,iter_t,context_t)
}

trace::rule_type const& trace_parser() {
    return trace::tr;
}

int main(int argc, char* argv[])
{
    std::string input("Trace address: AT123434");
    std::ifstream i(input);

    std::cout << "parsing: " << input << "\n";

    boost::spirit::istream_iterator b(i >> std::noskipws);
    boost::spirit::istream_iterator e = {};

    bool v = x3::phrase_parse(b, e, trace_parser(), x3::ascii::space);

    std::cout << "result: " << (v ? "OK" : "Failed") << "\n";
    return v;
}

编译输出

1>Source.cpp
1>H:\X\XXXXX\unit1.h(26,1): warning C5046: 'parse_rule': Symbol involving type with internal linkage not defined
1>Source.obj : error LNK2019: unresolved external symbol "bool __cdecl parse_rule<class boost::spirit::basic_istream_iterator<char,struct std::char_traits<char> >,struct boost::spirit::x3::context<struct trace::trace_parser,struct boost::spirit::x3::action<struct boost::spirit::x3::sequence<struct boost::spirit::x3::literal_string<char const *,struct boost::spirit::char_encoding::standard,struct boost::spirit::x3::unused_type>,struct boost::spirit::x3::rule<class p1,unsigned __int64,0> >,class <lambda_bca3e58e86871d1e58f1a6062ad05fd2> > const ,struct boost::spirit::x3::context<struct boost::spirit::x3::skipper_tag,struct boost::spirit::x3::char_class<struct boost::spirit::char_encoding::ascii,struct boost::spirit::x3::space_tag> const ,struct boost::spirit::x3::unused_type> > >(struct boost::spirit::x3::rule<class p1,unsigned __int64,0>,class boost::spirit::basic_istream_iterator<char,struct std::char_traits<char> > &,class boost::spirit::basic_istream_iterator<char,struct std::char_traits<char> > const &,struct boost::spirit::x3::context<struct trace::trace_parser,struct boost::spirit::x3::action<struct boost::spirit::x3::sequence<struct boost::spirit::x3::literal_string<char const *,struct boost::spirit::char_encoding::standard,struct boost::spirit::x3::unused_type>,struct boost::spirit::x3::rule<class p1,unsigned __int64,0> >,class <lambda_bca3e58e86871d1e58f1a6062ad05fd2> > const ,struct boost::spirit::x3::context<struct boost::spirit::x3::skipper_tag,struct boost::spirit::x3::char_class<struct boost::spirit::char_encoding::ascii,struct boost::spirit::x3::space_tag> const ,struct boost::spirit::x3::unused_type> > const &,unsigned __int64 &)" [...cut...] referenced in function "public: bool __cdecl boost::spirit::x3::rule<class p1,unsigned __int64,0>::parse<class boost::spirit::basic_istream_iterator<char,struct std::char_traits<char> >,struct boost::spirit::x3::context<struct trace::trace_parser,struct boost::spirit::x3::action<struct boost::spirit::x3::sequence<struct boost::spirit::x3::literal_string<char const *,struct boost::spirit::char_encoding::standard,struct boost::spirit::x3::unused_type>,struct boost::spirit::x3::rule<class p1,unsigned __int64,0> >,class <lambda_bca3e58e86871d1e58f1a6062ad05fd2> > const ,struct boost::spirit::x3::context<struct boost::spirit::x3::skipper_tag,struct boost::spirit::x3::char_class<struct boost::spirit::char_encoding::ascii,struct boost::spirit::x3::space_tag> const ,struct boost::spirit::x3::unused_type> >,unsigned __int64>(class boost::spirit::basic_istream_iterator<char,struct std::char_traits<char> > &,class boost::spirit::basic_istream_iterator<char,struct std::char_traits<char> > const &,struct boost::spirit::x3::context<struct trace::trace_parser,struct boost::spirit::x3::action<struct boost::spirit::x3::sequence<struct boost::spirit::x3::literal_string<char const *,struct boost::spirit::char_encoding::standard,struct boost::spirit::x3::unused_type>,struct boost::spirit::x3::rule<class p1,unsigned __int64,0> >,class <lambda_bca3e58e86871d1e58f1a6062ad05fd2> > const ,struct boost::spirit::x3::context<struct boost::spirit::x3::skipper_tag,struct boost::spirit::x3::char_class<struct boost::spirit::char_encoding::ascii,struct boost::spirit::x3::space_tag> const ,struct boost::spirit::x3::unused_type> > const &,struct boost::spirit::x3::unused_type,unsigned __int64 &)const " 

警告:

1>H:\X\XXXXX\unit1.h(26,1): warning C5046: 'parse_rule': Symbol involving type with internal linkage not defined

错误信息:

unresolved external symbol:

bool __cdecl parse_rule<iterator_t,trace_context_t>(
    parser1_t,
    iterator_t &,
    iterator_t const &,
    trace_context_t const &,
    unsigned __int64 &
) 

referenced in function 

public: 
bool __cdecl boost::spirit::x3::rule<class p1,unsigned __int64,0>::parse<
    iterator_t, trace_context_t, unsigned __int64>
(
    iterator_t &,
    iterator_t const &,
    trace_context_t const &,
    struct boost::spirit::x3::unused_type,
    unsigned __int64 &
)const   

Assuming the following shorcuts: 

using action_t = struct boost::spirit::x3::action<
                    struct boost::spirit::x3::sequence<
                        struct boost::spirit::x3::literal_string<
                            char const *,
                            struct boost::spirit::char_encoding::standard,
                            struct boost::spirit::x3::unused_type>,
                        struct boost::spirit::x3::rule<class p1,unsigned __int64,0>>,
                    class <lambda_bca3e58e86871d1e58f1a6062ad05fd2>>

using ctxt_t    = struct boost::spirit::x3::context<
                      struct boost::spirit::x3::skipper_tag,
                      struct boost::spirit::x3::char_class<
                          struct boost::spirit::char_encoding::ascii,
                          struct boost::spirit::x3::space_tag> const ,
                      struct boost::spirit::x3::unused_type>>

using iterator_t = class boost::spirit::basic_istream_iterator<char,struct std::char_traits<char> >

using trace_context_t = struct boost::spirit::x3::context<
                            struct trace::trace_parser,
                            action_t const ,
                            ctxt_t>

迁移到 Boost 1.74.0 解决了这个问题。