I am trying to reuse a parser (named parser1_rule
) that I have isolated in a translation unit (unit1.h/unit1.cpp), into another parser (name trace_parser
). However I get a warning and an error message (reproduced and reformated below for easier reading) stating more or less (this is my interpretation) that parser1_rule
have not been defined (or instantiated) for the new context trace_context_t
.
Indeed, in X3, rules have two template parameters: iterators and context. I thought when mixing two rules, one had to make sure they both use the same context and iterator's types. So I paid attention to use the same context for parser1_rule
and trace_parser
: context_type
. The same goes for the iterator, as recalled in X3: Linker Error (unresolved external symbol “parse_rule”) on nonterminal parser
But it seams this is not the way to go ? Shall I move the rule's definitions into an hpp file (which I wish I can avoid).
I created a minimal reproducible example to help understanding the issue:
EDIT: boost 1.71.0 and 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;
}
compilation output
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 "
Warning:
1>H:\X\XXXXX\unit1.h(26,1): warning C5046: 'parse_rule': Symbol involving type with internal linkage not defined
Error message:
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>
Move to Boost 1.74.0 solved the issue.