【问题标题】:Parse boost uuids with boost spirit用 boost 精神解析 boost uuid
【发布时间】:2019-06-04 09:04:36
【问题描述】:

我正在尝试使用boost::spirit::qi 编写一个boost::uuids::uuid 解析器,以便以一种很好的方式与其他qi 解析器一起使用它并拥有一个很好的统一解析器api。

我的第一个想法是编写一个自定义的qi::grammar,它将使用boost::conversion::try_lexical_convert<boost::uuids::uuid &, const std::string &>,但是这将存在将开始迭代器正确设置为使用位置的问题,因为boost::conversion::try_lexical_convert<boost::uuids::uuid &, const std::string &>不仅会匹配16个字符长的输入但也可以带括号或不带破折号。

我的第二种方法是使用boost::spirit::qi::rule(如果愿意,也可以使用从boost::spirit::qi::grammar::base_type 派生的语法CRTP),但随后出现编译错误,可能来自BOOST_FUSION_ADAPT_STRUCT 表达式:

    #include <iostream>
    #include <string>
    #include <cstdint>
    #include <boost/uuid/uuid.hpp>
    #include <boost/spirit/include/qi.hpp>


    BOOST_FUSION_ADAPT_STRUCT(
            boost::uuids::uuid,
            (uint8_t, data[0])
            (uint8_t, data[1])
            (uint8_t, data[2])
            (uint8_t, data[3])
            (uint8_t, data[4])
            (uint8_t, data[5])
            (uint8_t, data[6])
            (uint8_t, data[7])
            (uint8_t, data[8])
            (uint8_t, data[9])
            (uint8_t, data[10])
            (uint8_t, data[11])
            (uint8_t, data[12])
            (uint8_t, data[13])
            (uint8_t, data[14])
            (uint8_t, data[15])
    )

    template<typename Iterator>
    boost::spirit::qi::rule<Iterator, boost::uuids::uuid>
            uuid_internal_{
            boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    //time-low
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> -boost::spirit::qi::lit("-")
                    //time-mid
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> -boost::spirit::qi::lit("-")
                    //time-high-and-version
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> -boost::spirit::qi::lit("-")
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>() //clock-seq-and-reserved
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>() //clock-seq-low
                    >> -boost::spirit::qi::lit("-")
                    //node
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                    >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
    };

    template<typename Iterator>
    struct uuid_
            : ::boost::spirit::qi::grammar<Iterator, boost::uuids::uuid()>{
        uuid_() : uuid_::base_type(start) {

            start %= (boost::spirit::qi::lit("{") >> uuid_internal_ >> boost::spirit::qi::lit("}")) |
                     uuid_internal_ ;
        }

        boost::spirit::qi::rule<Iterator, boost::uuids::uuid()> start;

        boost::spirit::qi::rule<Iterator, boost::uuids::uuid()>
                uuid_internal_{
                boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        //time-low
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> -boost::spirit::qi::lit("-")
                        //time-mid
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> -boost::spirit::qi::lit("-")
                        //time-high-and-version
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> -boost::spirit::qi::lit("-")
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>() //clock-seq-and-reserved
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>() //clock-seq-low
                        >> -boost::spirit::qi::lit("-")
                        //node
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
                        >> boost::spirit::qi::uint_parser<uint8_t, 16, 1, 1>()
        };

    };

    int main() {
        std::string input;
        std::cin >> input;
        uuid_<std::string::const_iterator> uuid_{};
        boost::uuids::uuid uuid{};
        auto begin = input.begin(), end = input.end();

        const bool success = boost::spirit::qi::parse(begin, end, uuid_, uuid);
        if (!success || begin != end)
            throw std::runtime_error("Parsing failed");

        return 0;


    }

/opt/local/include/boost/spirit/home/support/container.hpp:292:15:错误:'boost::uuids::uuid'中没有名为'insert'的成员 c.insert(c.end(), val);

似乎是由boost::spirit::qi::detail::pass_through_container 生成的问题,但是我将BOOST_FUSION_ADAPT_ADT*(obj.begin()+n) 结合使用的方法也因多个错误而失败。

【问题讨论】:

    标签: boost uuid boost-spirit boost-spirit-qi boost-uuid


    【解决方案1】:

    您可以使用内置的 qi::stream 指令来获得 90% 的路径:

    uuid_ = qi::stream;
    start = '{' >> uuid_ >> '}' | uuid_;
    

    Live On Coliru

    #include <boost/spirit/include/qi.hpp>
    #include <boost/uuid/uuid_io.hpp>
    #include <iomanip>
    
    namespace qi = boost::spirit::qi;
    
    template <typename Iterator> struct uuid_type : ::qi::grammar<Iterator, boost::uuids::uuid()> {
        uuid_type() : uuid_type::base_type(start) {
    
            start = '{' >> uuid_ >> '}' | uuid_;
            uuid_ = qi::stream;
        }
      private:
        qi::rule<Iterator, boost::uuids::uuid()> start, uuid_;
    };
    
    int main() {
        uuid_type<std::string::const_iterator> uuid_{};
    
        for (std::string const input : {
                "2bc69ead-4aba-4a39-92c0-9565f4d464b4",
                "2BC69EAD-4ABA-4A39-92C0-9565F4D464B4",
                "{2bc69ead-4aba-4a39-92c0-9565f4d464b4}",
                "{2BC69EAD-4ABA-4A39-92C0-9565F4D464B4}",
                //"{2bc69ead--4aba--4a39----92c0--9565f4d464b4}",
                //"{2BC69EAD--4ABA--4A39----92C0--9565F4D464B4}",
                })
        {
            boost::uuids::uuid uuid{};
    
            std::cout << "==== Input " << std::quoted(input) << "\n";
    
            if (qi::parse(input.begin(), input.end(), uuid_ >> qi::eoi, uuid))
                std::cout << "Parsed " << uuid << "\n";
            else
                std::cout << "Parsing failed\n";
        }
    }
    

    打印

    ==== Input "2bc69ead-4aba-4a39-92c0-9565f4d464b4"
    Parsed 2bc69ead-4aba-4a39-92c0-9565f4d464b4
    ==== Input "2BC69EAD-4ABA-4A39-92C0-9565F4D464B4"
    Parsed 2bc69ead-4aba-4a39-92c0-9565f4d464b4
    ==== Input "{2bc69ead-4aba-4a39-92c0-9565f4d464b4}"
    Parsed 2bc69ead-4aba-4a39-92c0-9565f4d464b4
    ==== Input "{2BC69EAD-4ABA-4A39-92C0-9565F4D464B4}"
    Parsed 2bc69ead-4aba-4a39-92c0-9565f4d464b4
    

    剩下的 10%

    根据modified Pareto principle,剩下的 10% 是困难的部分。

    我什至不确定你是否想要这个,但+qi::lit("-") 暗示评论的测试用例也应该被接受(?!):

            //"{2bc69ead--4aba--4a39----92c0--9565f4d464b4}",
            //"{2BC69EAD--4ABA--4A39----92C0--9565F4D464B4}",
    

    如果那是 /really/ 你想要的,我确实建议使用词法转换实现进行两阶段解析操作:

    好的,因为现在已经过了一个多小时,这意味着它更像是“10% 的功能需要 900% 的努力”——我希望你真的想要它 :)

    Live On Coliru

    #include <boost/spirit/include/qi.hpp>
    #include <boost/lexical_cast.hpp>
    #include <boost/uuid/uuid_io.hpp>
    #include <iomanip>
    
    using Uuid = boost::uuids::uuid;
    
    namespace boost::spirit::traits {
        template <> struct is_container<Uuid> : mpl::false_ {};
    
        template <> struct assign_to_attribute_from_value<Uuid, std::string> {
            static void call(std::string const& s, Uuid& v) { v = lexical_cast<Uuid>(s); }
        };
    }
    
    namespace qi = boost::spirit::qi;
    
    template <typename Iterator> struct uuid_type : qi::grammar<Iterator, Uuid()> {
        uuid_type() : uuid_type::base_type(start) {
            using namespace qi;
    
            auto sep_  = copy(+lit('-') >> qi::attr('-'));
            auto hex2_ = copy(xdigit >> xdigit >> xdigit >> xdigit);
            auto hex4_ = copy(hex2_ >> hex2_);
            auto hex6_ = copy(hex4_ >> hex2_);
            auto fmt_  = copy(
                hex4_ >> sep_ >> hex2_ >> sep_ >> hex2_ >> sep_ >> hex2_ >> sep_ >> hex6_
            );
    
            start = as_string['{' >> fmt_ >> '}' | fmt_];
        }
      private:
        qi::rule<Iterator, Uuid()> start;
    };
    
    int main() {
        uuid_type<std::string::const_iterator> uuid_{};
    
        for (std::string const input : {
                "2bc69ead-4aba-4a39-92c0-9565f4d464b4",
                "2BC69EAD-4ABA-4A39-92C0-9565F4D464B4",
                "{2bc69ead-4aba-4a39-92c0-9565f4d464b4}",
                "{2BC69EAD-4ABA-4A39-92C0-9565F4D464B4}",
                "{2bc69ead--4aba--4a39----92c0--9565f4d464b4}",
                "{2BC69EAD--4ABA--4A39----92C0--9565F4D464B4}",
                })
        {
            Uuid uuid{};
    
            std::cout << "==== Input " << std::quoted(input) << "\n";
    
            auto f = input.begin(), l = input.end();
            if (qi::parse(f, l, uuid_ >> qi::eoi, uuid))
                std::cout << "Parsed " << uuid << "\n";
            else
                std::cout << "Parsing failed\n";
        }
    }
    

    打印

    ==== Input "2bc69ead-4aba-4a39-92c0-9565f4d464b4"
    Parsed 2bc69ead-4aba-4a39-92c0-9565f4d464b4
    ==== Input "2BC69EAD-4ABA-4A39-92C0-9565F4D464B4"
    Parsed 2bc69ead-4aba-4a39-92c0-9565f4d464b4
    ==== Input "{2bc69ead-4aba-4a39-92c0-9565f4d464b4}"
    Parsed 2bc69ead-4aba-4a39-92c0-9565f4d464b4
    ==== Input "{2BC69EAD-4ABA-4A39-92C0-9565F4D464B4}"
    Parsed 2bc69ead-4aba-4a39-92c0-9565f4d464b4
    ==== Input "{2bc69ead--4aba--4a39----92c0--9565f4d464b4}"
    Parsed 2bc69ead-4aba-4a39-92c0-9565f4d464b4
    ==== Input "{2BC69EAD--4ABA--4A39----92C0--9565F4D464B4}"
    Parsed 2bc69ead-4aba-4a39-92c0-9565f4d464b4
    

    【讨论】:

    • 哎呀。终于设法让另一部分也能正常工作Live On Coliru
    • 哦,不,我再次混淆了 Plus 和可选 (-) 解析器运算符。当然应该是-qi::lit("-")我会更新这个问题。但是再次非常感谢你!
    • 但也很有趣看到你的方法没有漂亮的 >> 运算符快捷方式。无论流解析器使用现有的 >> 运算符重载,还是从精神解析器中生成一个,我总是有点困惑。
    • 流解析器使用operator&gt;&gt;,这正是lexical_cast默认使用的
    • 确实如此。 Moving他们。干杯
    猜你喜欢
    • 2012-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-27
    • 1970-01-01
    • 2017-12-26
    相关资源
    最近更新 更多