简体   繁体   中英

X3: Linker Error (unresolved external symbol “parse_rule”) on nonterminal parser

First of all I am using MSVC 2017 (latest version). Here is my code for the nonterminal parser:

player.hpp

namespace parse
{
    namespace impl
    {
        namespace x3 = boost::spirit::x3;

        struct _tag;

        using player_type = x3::rule<_tag, PlayerIterator>;
        using player_vector_type = x3::rule<_tag, std::vector<PlayerIterator>>;
        BOOST_SPIRIT_DECLARE(player_type);
        BOOST_SPIRIT_DECLARE(player_vector_type);
    }; //impl

    impl::player_type player();
    impl::player_vector_type player_vector();
}; //parse

player.cpp

namespace parse
{
    namespace impl
    {
        const player_type player = "player";
        const player_vector_type player_vector = "player_vector";
        auto player_find = [](auto &ctx)
        {
            auto &attr = x3::_attr(ctx);
            if(attr.which() == 0)
                return x3::_val(ctx) = PlayerManager::find(boost::get<int>(attr));
            return x3::_val(ctx) = PlayerManager::find(boost::get<std::string>(attr));
        };
        auto player_vector_find = [](auto &ctx)
        {
            return x3::_val(ctx) = PlayerManager::vector_find(x3::_attr(ctx));
        };
        auto const player_def = (x3::int_ | (+x3::char_))[player_find];
        auto const player_vector_def = (((+x3::char_)[player_vector_find]));
        BOOST_SPIRIT_DEFINE(player);
        BOOST_SPIRIT_DEFINE(player_vector);
        BOOST_SPIRIT_INSTANTIATE(player_type, iterator_type, context_type);
        BOOST_SPIRIT_INSTANTIATE(player_vector_type, iterator_type, context_type);
    } //impl
    parse::impl::player_type player() { return impl::player; }
    parse::impl::player_vector_type player_vector() { return impl::player_vector; }
}//parse

I get linker LNK2019 errors about "unresolved external symbols referenced":
Pastebin.com link with the errors Any ideas about them? Thanks in advance.

EDIT: That's how I call it in my source file:

void test(std::string &params)
{
    std::tuple<PlayerIterator, std::vector<PlayerIterator>, std::string> tuple;
    if (!x3::phrase_parse(params.begin(), params.end(), parse::player()>> parse::player_vector() >> (+x3::char_), x3::space,tuple))
    {
        std::cout << "Error: Parsing failed" << std::endl;
        return;
    }
    std::cout << "Parsing succeded" << std::endl;
    std::cout << "Found player, size of player vector: "<< std::get<1>(tuple).size() << ", also parsed string:" << std::get<2>(tuple);
    return;
};

I'm willing to bet $10 that you mismatched the context or iterator types on the instantiations.

Eg in your test function, the argument is std::string& , hence params.begin() will be std::string::iterator . If you had the iterator_type configured as follows:

using iterator_type = std::string::const_iterator; // very sensible!

you would have unresolved externals because the iterator type doesn't match the one actually required.

Same thing for the context. To match your invocation it needs to be exactly:

using context_type = x3::phrase_parse_context<x3::space_type>::type;

Sadly you didn't show the whole code, so you'll have to check on your own.

Notes

  1. re-using the tag type is recipe for disaster. I don't think it can work. The rule tags are what dispatches the implementation function in the case of separated compilation units. Fix it:

     using player_type = x3::rule<struct player_tag, PlayerIterator>; using player_vector_type = x3::rule<struct player_vector_tag, std::vector<PlayerIterator>>; 
  2. copying the rules seems wasteful, consider returning by reference:

    impl::player_type const& player(); impl::player_vector_type const& player_vector();

    Note: this should be fine wrt static initialization order fiasco

  3. using which() on a variant is an anti-pattern. You can replace

     auto player_find = [](auto &ctx) { auto &attr = x3::_attr(ctx); if (attr.which() == 0) return x3::_val(ctx) = PlayerManager::find(boost::get<int>(attr)); return x3::_val(ctx) = PlayerManager::find(boost::get<std::string>(attr)); }; 

    With

     auto find = [](auto const& key) { return PlayerManager::find(key); }; auto player_find = [](auto &ctx) { return x3::_val(ctx) = boost::apply_visitor(find, x3::_attr(ctx)); }; 
  4. (+x3::char_) always matches all input

  5. (+x3::graph) still matches all input because of the skipper
  6. Instead you wanted a lexeme:

     auto const name = x3::lexeme[+x3::graph]; auto const player_def = (x3::int_ | name) [player_find]; auto const player_vector_def = name[ player_vector_find]; 
  7. May I suggest to write the test function a lot more concisely:

     void test(std::string const &params) { auto comment_ = x3::lexeme[+x3::char_]; PlayerIterator player; PlayerIterators vec; std::string comment; auto tuple = std::tie(player, vec, comment); if (phrase_parse(params.cbegin(), params.cend(), parse::player() >> parse::player_vector() >> comment_, x3::space, tuple)) { std::cout << "Parsing succeded" << std::endl; std::cout << "Found player, size of player vector: " << vec.size() << "\\n"; std::cout << "Also parsed string: " << std::quoted(comment); } else { std::cout << "Error: Parsing failed" << std::endl; } } 

Full Demo

See it Live On Wandbox

  • stuff.h

    Contains mockup PlayerManager

     #pragma once #include <string> #include <vector> #include <iostream> struct PlayerIterator { }; using PlayerIterators = std::vector<PlayerIterator>; struct PlayerManager { static PlayerIterator find(std::string const&) { std::cout << __PRETTY_FUNCTION__ << "\\n"; return {}; } static PlayerIterator find(int) { std::cout << __PRETTY_FUNCTION__ << "\\n"; return {}; } static PlayerIterators vector_find(std::string const&) { std::cout << __PRETTY_FUNCTION__ << "\\n"; return {}; } }; 
  • test.h

     #pragma once #include <boost/spirit/home/x3.hpp> #include <boost/fusion/adapted.hpp> #include "stuff.h" namespace x3 = boost::spirit::x3; namespace parse { namespace impl { using player_type = x3::rule<struct player_tag, PlayerIterator>; using player_vector_type = x3::rule<struct player_vector_tag, PlayerIterators>; BOOST_SPIRIT_DECLARE(player_type) BOOST_SPIRIT_DECLARE(player_vector_type) } //impl impl::player_type const& player(); impl::player_vector_type const& player_vector(); } //parse 
  • test.cpp

     #include "stuff.h" #include "test.h" using iterator_type = std::string::const_iterator; using context_type = x3::phrase_parse_context<x3::space_type>::type; namespace parse { namespace impl { const player_type player = "player"; const player_vector_type player_vector = "player_vector"; auto find = [](auto const& key) { return PlayerManager::find(key); } ; auto player_find = [](auto &ctx) { return x3::_val(ctx) = boost::apply_visitor(find, x3::_attr(ctx)); } ; auto player_vector_find = [](auto &ctx) { return x3::_val(ctx) = PlayerManager::vector_find(x3::_attr(ctx)); } ; auto const name = x3::lexeme[+x3::graph]; auto const player_def = (x3::int_ | name) [player_find]; auto const player_vector_def = name[ player_vector_find]; BOOST_SPIRIT_DEFINE(player) BOOST_SPIRIT_DEFINE(player_vector) BOOST_SPIRIT_INSTANTIATE(player_type, iterator_type, context_type) BOOST_SPIRIT_INSTANTIATE(player_vector_type, iterator_type, context_type) } // namespace impl parse::impl::player_type const& player() { return impl::player; } parse::impl::player_vector_type const& player_vector() { return impl::player_vector; } } // namespace parse 
  • main.cpp

     #include "stuff.h" #include "test.h" #include <iostream> #include <iomanip> void test(std::string const &params) { auto comment_ = x3::lexeme[+x3::char_]; PlayerIterator player; PlayerIterators vec; std::string comment; auto tuple = std::tie(player, vec, comment); if (phrase_parse(params.cbegin(), params.cend(), parse::player() >> parse::player_vector() >> comment_, x3::space, tuple)) { std::cout << "Parsing succeded" << std::endl; std::cout << "Found player, size of player vector: " << vec.size() << "\\n"; std::cout << "Also parsed string: " << std::quoted(comment); } else { std::cout << "Error: Parsing failed" << std::endl; } } int main() { test("42 someword # bogus trailing comment"); } 

Prints:

static PlayerIterator PlayerManager::find(int)
static PlayerIterators PlayerManager::vector_find(const std::string &)
Parsing succeded
Found player, size of player vector: 0
Also parsed string: "# bogus trailing comment"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM