Boost.Spirit:将字符串对从 Qi 移植到 X3

Boost.Spirit: porting string pairs from Qi to X3

我有以下工作 Qi 代码:

struct query_grammar
    : public boost::spirit::qi::grammar<Iterator, string_map<std::string>()>
{
  query_grammar() : query_grammar::base_type(query)
  {
    query = pair >> *(boost::spirit::qi::lit('&') >> pair);
    pair = +qchar >> -(boost::spirit::qi::lit('=') >> +qchar);
    qchar = ~boost::spirit::qi::char_("&=");
  }

  boost::spirit::qi::rule<Iterator, std::map<std::string,std::string>()> query;
  boost::spirit::qi::rule<Iterator, std::map<std::string,std::string>::value_type()> pair;
  boost::spirit::qi::rule<Iterator, char()> qchar;
};

我尝试将其移植到 x3:

namespace x3 = boost::spirit::x3;
const x3::rule<class query_char_, char> query_char_ = "query_char";
const x3::rule<class string_pair_, std::map<std::string,std::string>::value_type> string_pair_ = "string_pair";
const x3::rule<class string_map_, std::map<std::string,std::string>> string_map_ = "string_map";

const auto query_char__def = ~boost::spirit::x3::char_("&=");
const auto string_pair__def = +query_char_ >> -(boost::spirit::x3::lit('=') >> +query_char_);
const auto string_map__def = string_pair_ >> *(boost::spirit::x3::lit('&') >> string_pair_);

BOOST_SPIRIT_DEFINE(string_map_)
BOOST_SPIRIT_DEFINE(string_pair_)
BOOST_SPIRIT_DEFINE(query_char_)

但我在尝试使用 string_map_ 解析字符串时收到以下错误:

/usr/include/boost/spirit/home/x3/support/traits/move_to.hpp:209: erreur : no matching function for call to move_to(const char*&, const char*&, std::pair<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&, boost::mpl::identity<boost::spirit::x3::traits::plain_attribute>::type) 
     detail::move_to(first, last, dest, typename attribute_category<Dest>::type());
     ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

我看到了这个答案: 并尝试将我的 string_pair 变成原始的但无济于事。


编辑:

spirit examples 中的示例代码也无法编译,所以我想问题有点深:

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

namespace x3 = boost::spirit::x3;
int main()
{
  std::string input( "cosmic  pizza  " );
  auto iter = input.begin();
  auto end_iter = input.end();
  std::pair<std::string, std::string> result;
  x3::parse( iter, end_iter, *(~x3::char_(' ')) >> ' ' >> *x3::char_, result);
}

补气

首先,我必须用 Qi 变体修复规则声明才能工作:

qi::rule<Iterator, std::pair<std::string,std::string>()> pair;

原因很简单,value_typepair<key_type const, mapped_type>,这是永远无法分配的。

这是齐 SSCCE:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <map>

namespace qi = boost::spirit::qi;

template <typename T> using string_map = std::map<T, T>;

template <typename Iterator>
struct query_grammar : public qi::grammar<Iterator, string_map<std::string>()>
{
    query_grammar() : query_grammar::base_type(query)
    {
        qchar = ~qi::char_("&=");
        pair  = +qchar >> -(qi::lit('=') >> +qchar);
        query = pair >> *(qi::lit('&') >> pair);
    }

  private:
    qi::rule<Iterator, std::map<std::string,std::string>()> query;
    qi::rule<Iterator, std::pair<std::string,std::string>()> pair;
    qi::rule<Iterator, char()> qchar;
};

int main() {
    using It = std::string::const_iterator;
    for (std::string const input : { "foo=bar&baz=boo" })
    {
        std::cout << "======= " << input << "\n";
        It f = input.begin(), l = input.end();
        string_map<std::string> sm;
        if (parse(f, l, query_grammar<It>{}, sm)) {
            std::cout << "Parsed " << sm.size() << " pairs\n";
        } else {
            std::cout << "Parse failed\n";
        }

        if (f != l)
            std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
    }
}

版画

======= foo=bar&baz=boo
Parsed 2 pairs

改善气机

以下更简单的语法似乎更好:

Live On Coliru

template <typename Iterator, typename T = std::string>
struct query_grammar : public qi::grammar<Iterator, string_map<T>()>
{
    query_grammar() : query_grammar::base_type(query) {
        using namespace qi;
        pair  =  +~char_("&=") >> '=' >> *~char_("&");
        query = pair % '&';
    }

  private:
    qi::rule<Iterator, std::pair<T,T>()> pair;
    qi::rule<Iterator, std::map<T,T>()> query;
};

它接受空值(例如 &q=&x=)和包含额外 = 的值:&q=7==8&rt=bool。它的效率可能会高得多(未测试)。

X3版本

没看你的代码,直接翻译成了X3版本:

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <iostream>
#include <map>

namespace x3 = boost::spirit::x3;

template <typename T> using string_map = std::map<T, T>;

namespace grammar {
    using namespace x3;
    auto pair  =  +~char_("&=") >> '=' >> *~char_("&");
    auto query = pair % '&';
}

int main() {
    using It = std::string::const_iterator;
    for (std::string const input : { "foo=bar&baz=boo" })
    {
        std::cout << "======= " << input << "\n";
        It f = input.begin(), l = input.end();
        string_map<std::string> sm;
        if (parse(f, l, grammar::query, sm)) {
            std::cout << "Parsed " << sm.size() << " pairs\n";
        } else {
            std::cout << "Parse failed\n";
        }

        if (f != l)
            std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
    }
}

显然 ( --- ) 打印

======= foo=bar&baz=boo
Parsed 2 pairs

X3 改进

您可能希望为规则强制属性类型,因为自动属性传播可能具有令人惊讶的启发式方法。

namespace grammar {

    template <typename T = std::string> auto& query() {
        using namespace x3;

        static const auto s_pair  
            = rule<struct pair_, std::pair<T, T> > {"pair"} 
            = +~char_("&=") >> -('=' >> *~char_("&"));
        static const auto s_query
            = rule<struct query_, std::map<T, T> > {"query"}
            = s_pair % '&';

        return s_query;
    };

}

看到了Live On Coliru

哪里出了问题?

X3 版本在 std::map<>::value_type

中遇到了与 const 键类型相同的问题