linking errors while separate parser using boost spirit x3
Asked Answered
N

2

4

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 a linking error. HERE is my parser which is separated.

The linker error reads

<artificial>:(.text.startup+0xbb): undefined reference to `bool kyle::parser::impl::parse_rule<__gnu_cxx::__normal_iterator<char*, 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>, boost::spirit::x3::unused_type const>(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*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&, __gnu_cxx::__normal_iterator<char*, 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&, boost::spirit::x3::unused_type const&)'

What am I doing wrong? How can I make my example work?

  • 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::ascii::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 str, Parser&& p, 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);
        ret &= (!full_match || (in == end));
        return ret;
    
    }
    
    int main(){
        auto b = test("fobar", kyle::parser::identifier());
        std::cout << b << std::endl;
    
    }
    
Nates answered 8/11, 2016 at 21:3 Comment(0)
C
8

Two points:

  1. 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> >&)'

  2. 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

FULL CODE

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)
    
Constrain answered 9/11, 2016 at 0:18 Comment(5)
FYI: Back when I still streamed my SO answering sessions on livecoding.tv/sehe/videos you would have been able to admire just how long it took for me to find this answer. (Hint it's not fast)Constrain
Forgot about some other details. Added live demo.Constrain
Is there any issue with your live coding videos? I can't seem to get any of your videos playing from chrome. I distinctly remember watching one of those a year back.Veilleux
Showing where the mystake can be seen in the error message is really helpfull for others as this is still today a real pain to debug these error messages.Misname
Though, for non experts, it is not clear where in the code the iterator_type is getting deduced incorrectly, nor where it is fixedMisname
S
5

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.

Selfexamination answered 9/11, 2016 at 23:45 Comment(2)
Same comes in handy for the iterator types, I suppose. I use the same trick. This time it took me a while to unfold the macros to what they are actually achieving. I'm not a big fan of the macros for this reason.Constrain
This. This is what I need. 2 errors: one that lists needed type and one that lists actually compiled type.Crystalcrystalline

© 2022 - 2024 — McMap. All rights reserved.