I am currentyl trying to separate my boost spirit x3 parser into different _def and .cpp files using BOOST_SPIRIT_DEFINE/DECLARE/INSTANTIATE
, but I keep getting
Two points:
You define the context as
typedef x3::phrase_parse_context<x3::space_type>::type context_type;
However, you try to invoke it with x3::space
instead of x3::ascii::space
.
The hint was in the error message that you didn't include:
/home/sehe/custom/boost/boost/spirit/home/x3/nonterminal/rule.hpp:116: undefined reference to 'bool kyle::parser::impl::parse_rule<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::spirit::x3::context<boost::spirit::x3::skipper_tag, boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::space_tag> const, boost::spirit::x3::unused_type>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(boost::spirit::x3::rule<kyle::parser::impl::identifier_class, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, false>, __gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&, __gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&, boost::spirit::x3::context<boost::spirit::x3::skipper_tag, boost::spirit::x3::char_class<boost::spirit::
char_encoding::standard
, boost::spirit::x3::space_tag> const, boost::spirit::x3::unused_type> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)'
Iterator type is getting deduced as std::string::iterator
, not std::string::const_iterator
. Fix it or don't use auto
always:
Live On Melpon
For posterity
config.hpp:
#include <boost/spirit/home/x3.hpp>
namespace kyle{
namespace parser{
namespace x3 = boost::spirit::x3;
typedef std::string::const_iterator iterator_type;
typedef x3::phrase_parse_context<x3::space_type>::type context_type;
}
}
literals.cpp:
#include "literals_def.hpp"
#include "config.hpp"
#include <boost/spirit/home/x3.hpp>
namespace kyle { namespace parser { namespace impl {
BOOST_SPIRIT_INSTANTIATE(identifier_type, iterator_type, context_type);
} } }
literals_def.hpp:
#include <boost/spirit/home/x3.hpp>
#include "literals.hpp"
namespace kyle {
namespace parser {
namespace impl {
namespace x3 = boost::spirit::x3;
const identifier_type identifier = "identifier";
auto const identifier_def = x3::alpha >> *x3::alnum;
BOOST_SPIRIT_DEFINE(identifier)
}
impl::identifier_type identifier(){
return impl::identifier;
}
}
}
literals.hpp:
#include <boost/spirit/home/x3.hpp>
namespace kyle{
namespace parser{
namespace impl {
namespace x3 = boost::spirit::x3;
struct identifier_class;
typedef x3::rule<identifier_class, std::string> identifier_type;
BOOST_SPIRIT_DECLARE(identifier_type)
}
impl::identifier_type identifier();
}
}
main.cpp:
#include "literals.hpp"
#include <iostream>
template<typename Parser>
bool test(std::string const& str, Parser p, std::string& output, bool full_match = true)
{
auto in = str.begin();
auto end = str.end();
bool ret = boost::spirit::x3::phrase_parse(in, end, p, boost::spirit::x3::space, output);
ret &= (!full_match || (in == end));
return ret;
}
int main(){
std::string s;
auto b = test("fobar", kyle::parser::identifier(), s);
std::cout << b << ": " << s << std::endl;
}
CMakeLists.txt:
ADD_EXECUTABLE(sox3 main.cpp literals.cpp)
SET(CMAKE_CXX_COMPILER g++-5)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem /home/sehe/custom/boost -std=c++14 -O3 -pthread -march=native -flto)
In addition to sehe's answer, let me add that:
You'll need to be very exact with what you declare and with what you actually use. X3 allows any sort of possible types, but the linker does not.
Here's a tip: When having linker errors, after BOOST_SPIRIT_INSTANTIATE, declare something like:
int x = context_type{};
It will be an error, yes, because context_type cannot be converted to an int. But that error will also give you the exact type of your context. Compare it against the linker error and you will see your mistake.