X3:非终端解析器上的链接器错误(未解析的外部符号 "parse_rule")

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

首先,我使用的是 MSVC 2017(最新版本)。 这是我的非终端解析器代码:

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

我收到有关 "unresolved external symbols referenced":
的链接器 LNK2019 错误 Pastebin.com link with the errors 关于他们的任何想法? 提前致谢。

编辑: 这就是我在源文件中的调用方式:

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;
};

我愿意打赌 10 美元,你不匹配实例化的上下文或迭代器类型。

例如在您的 test 函数中,参数是 std::string&,因此 params.begin() 将是 std::string::iterator。如果您的 iterator_type 配置如下:

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

你会有未解决的外部问题,因为迭代器类型与实际需要的不匹配。

上下文相同。要匹配您的调用,它需要完全是:

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

遗憾的是你没有展示完整的代码,所以你必须自己检查。

注释

  1. 重复使用标签类型会导致灾难。我不认为它 可以 工作。规则标签是在分离的编译单元的情况下调度实现函数的东西。修复它:

    using player_type        = x3::rule<struct player_tag,        PlayerIterator>;
    using player_vector_type = x3::rule<struct player_vector_tag, std::vector<PlayerIterator>>;
    
  2. 复制规则似乎很浪费,考虑引用返回:

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

    注意:这应该没问题w.r.t。 static initialization order fiasco

  3. 在变体上使用 which() 是一种反模式。你可以替换

    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 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_) 总是匹配所有输入

  5. (+x3::graph) 仍然匹配所有输入,因为船长
  6. 相反,你想要一个词素:

    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. 我可以建议将 test 函数写得更简洁:

    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;
        }
    }
    

完整演示

看到了Live On Wandbox

  • stuff.h

    包含模型 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");
    }
    

打印:

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"