为什么我的组件编译为容器但 spirit::x3::int_parser 没有?

Why does my component compile as a container but spirit::x3::int_parser does not?

我一直致力于更好地理解编译时代码,尤其是 spirit::x3。我有一个 class my_uuid,用 x3::rule 编译它工作正常。但是由于 Iterator 是在范围之外声明的,所以它只适用于 std::string::iterator。所以我想我只是创建一个像 x3::int_parser 这样写的组件。据我所知,它具有完全相同的签名。我试过将它放在 x3 命名空间中,并在 `..\x3\numeric\int.hpp' header 之后包含它,我认为这是有充分理由的,但它让我望而却步。

如果我在我的 uuid_parser 和 int_parser 中设置一个断点,我可以看到 int_parser 被单独留下,从解析器序列中调用。但是我的解析器被包裹起来,就好像它是一个容器一样。如果我在第 40 行取消对解析器的注释,编译器就会开始寻找 insert 之类的内容。所以,这更令人困惑,如果我将它保留为序列中的最后一个组件,为什么它不根据 insertmove 的需要进行编译?

我似乎遗漏了一些非常基本的东西。我的问题不是如何解决这个问题,而是我错过了什么。谢谢。

#include<iostream>
#include <vector>

#include <boost/spirit/home/x3.hpp>

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

namespace x3 = boost::spirit::x3;

struct uuid_parser : x3::parser<uuid_parser> {
    typedef boost::uuids::uuid attribute_type;
    static bool const has_attribute = true;
    static bool const handles_container = false;

    template <typename Iterator, typename Context, typename Attribute>
    bool parse(Iterator& first, Iterator const& last, Context const& context, x3::unused_type, Attribute& uuid) const {
        boost::iterator_range<Iterator> rng;
        auto ret = x3::parse(first, last, x3::raw[*x3::char_("0-9a-fA-F-")], rng);
        try {
            uuid = boost::uuids::string_generator()(rng.begin(), rng.end());
        }
        catch (std::exception& e) {
            boost::ignore_unused(e);
            return false;
        }
        return true;
    }
};
const uuid_parser uuid_ = {};

int main() {
    std::string uuid_str(R"(    id(78461bab-6d7c-48bc-a79a-b716d1ab97cb
    id(9350cf32-7fe5-40d2-8a0d-c7f7562d7a15
    id(bad-9350cf32-7fe5-40d2-8a0d-c7f7562d7a15
)");
    auto first(uuid_str.begin());
    std::vector<boost::uuids::uuid> attr;
    x3::phrase_parse(first, uuid_str.end(), *(x3::lit("id(") >> uuid_), x3::space, attr);
    //x3::phrase_parse(first, uuid_str.end(), *(x3::lit("uid(") >> uuid_ >> ')'), x3::space, attr);
    for (auto& item : attr)
        std::cout << item << std::endl;

    std::string int_str(" x(1) x(2) x(3)"); auto ibegin(int_str.begin());
    std::vector<int> int_vect;
    x3::phrase_parse(ibegin, int_str.end(), *(x3::lit("x(") >> x3::int_ >> ')'), x3::space, int_vect);
    for (auto& item : int_vect)
        std::cout << item << std::endl;
    return 0;
}

令人困惑的是 uuids::uuid 看起来像一个容器。您可以通过添加以下内容来观察这一点:

template <> struct x3::traits::detail::is_container_impl<boost::uuids::uuid> {
    using type = std::false_type;
};

Live On Coliru

#include <iostream>
#include <vector>

#include <boost/spirit/home/x3.hpp>

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

namespace x3 = boost::spirit::x3;

struct uuid_parser : x3::parser<uuid_parser> {
    using attribute_type = boost::uuids::uuid;
    static bool const has_attribute = true;
    static bool const handles_container = false;

    template <typename Iterator, typename Context, typename Attribute>
    bool parse(Iterator& first, Iterator const& last, Context const& context,
               x3::unused_type, Attribute& uuid) const
    {
        try {
            boost::iterator_range<Iterator> rng;
            auto ret = x3::parse(first, last, x3::raw[*(x3::xdigit | '-')], rng);
            if (ret)
                uuid = boost::uuids::string_generator()(rng.begin(), rng.end());
            return true;
        } catch (std::exception const&) {
            return false;
        }
    }
};

template <> struct x3::traits::detail::is_container_impl<boost::uuids::uuid> {
    using type = std::false_type;
};

int main() {
    const uuid_parser uuid_ = {};
    std::string uuid_str(R"(    uid(78461bab-6d7c-48bc-a79a-b716d1ab97cb)
        uid(9350cf32-7fe5-40d2-8a0d-c7f7562d7a15)
        uid(bad-9350cf32-7fe5-40d2-8a0d-c7f7562d7a15)
    )");

    std::vector<boost::uuids::uuid> vec;
    x3::phrase_parse(uuid_str.begin(), uuid_str.end(), *("uid(" >> uuid_ >> ")"), x3::space, vec);
    for (auto const& item : vec)
        std::cout << item << std::endl;
}

版画

78461bab-6d7c-48bc-a79a-b716d1ab97cb
9350cf32-7fe5-40d2-8a0d-c7f7562d7a15

添加一些可调试性

Live On Coliru

auto u = x3::rule<struct _u, boost::uuids::uuid>{"u"} = uuid_;
auto r = x3::rule<struct _r, boost::uuids::uuid>{"r"} = "uid(" >> u >> ")";
x3::phrase_parse(uuid_str.begin(), uuid_str.end(), *r, x3::space, vec);

版画

<r>
  <try>    uid(78461bab-6d7</try>
  <u>
    <try>78461bab-6d7c-48bc-a</try>
    <success>)\n        uid(9350cf</success>
    <attributes>78461bab-6d7c-48bc-a79a-b716d1ab97cb</attributes>
  </u>
  <success>\n        uid(9350cf3</success>
  <attributes>78461bab-6d7c-48bc-a79a-b716d1ab97cb</attributes>
</r>
<r>
  <try>\n        uid(9350cf3</try>
  <u>
    <try>9350cf32-7fe5-40d2-8</try>
    <success>)\n        uid(bad-93</success>
    <attributes>9350cf32-7fe5-40d2-8a0d-c7f7562d7a15</attributes>
  </u>
  <success>\n        uid(bad-935</success>
  <attributes>9350cf32-7fe5-40d2-8a0d-c7f7562d7a15</attributes>
</r>
<r>
  <try>\n        uid(bad-935</try>
  <u>
    <try>bad-9350cf32-7fe5-40</try>
    <fail/>
  </u>
  <fail/>
</r>
78461bab-6d7c-48bc-a79a-b716d1ab97cb
9350cf32-7fe5-40d2-8a0d-c7f7562d7a15

简化

事实上,如您所见,您 不需要 使用 BOOST_SPIRIT_DEFINE,完全将实例化与声明分开:

Live On Coliru

#include <iostream>
#include <vector>

#include <boost/spirit/home/x3.hpp>

#include <boost/lexical_cast/try_lexical_convert.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

namespace x3 = boost::spirit::x3;

template <> struct x3::traits::detail::is_container_impl<boost::uuids::uuid> {
    using type = std::false_type;
};

namespace Parser {
    template <typename T> static inline auto as(auto p) {
        return x3::rule<struct _, T>{"as"} = p;
    }

    static inline auto const to_uuid = [](auto& ctx) {
        auto r = _attr(ctx);
        _pass(ctx) = boost::conversion::try_lexical_convert<boost::uuids::uuid>(
            std::string_view{r.begin(), r.end()}, _val(ctx));
    };

    static inline auto const uuid_ =
        as<boost::uuids::uuid>(x3::raw[*(x3::xdigit | '-')][to_uuid]);
} // namespace Parser

int main() {
    std::string uuid_str(R"(    uid(78461bab-6d7c-48bc-a79a-b716d1ab97cb)
        uid(9350cf32-7fe5-40d2-8a0d-c7f7562d7a15)
        uid(bad-9350cf32-7fe5-40d2-8a0d-c7f7562d7a15)
    )");

    std::vector<boost::uuids::uuid> vec;
    x3::phrase_parse(uuid_str.begin(), uuid_str.end(), *("uid(" >> Parser::uuid_ >> ")"), x3::space, vec);
    for (auto const& item : vec)
        std::cout << item << std::endl;
}

版画

78461bab-6d7c-48bc-a79a-b716d1ab97cb
9350cf32-7fe5-40d2-8a0d-c7f7562d7a15

此外,此外...

你说“迭代器”是硬编码的。事实上,它是。但请注意,这仅用于显式模板专业化。没有什么能阻止您专注于多种迭代器类型!

参见例如