让 boost::spirit::qi 使用 stl 容器 [英] Getting boost::spirit::qi to use stl containers

查看:19
本文介绍了让 boost::spirit::qi 使用 stl 容器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 boost.spirit 的 qi 库解析某些内容,但遇到了一个问题.根据 精神文档, a >>b 应该产生类型为 tuple 的东西.但这是一个 boost::tuple(又名融合向量),而不是 std::tuple(我想要的).

I'm trying to parse something with boost.spirit's qi library, and I'm running into an issue. According to the spirit docs, a >> b should produce something with the type tuple<A, B>. But this is a boost::tuple (aka fusion vector), and not a std::tuple (which I want).

有没有什么简单的方法可以在 boost::tuple => std::tuple 之间进行这种转换?

Is there any easy way to make this conversion between boost::tuple => std::tuple?

同一个文档页面说 *a 应该产生类型为 vector 的东西.这似乎正在生成一个 std::vector(或某种可以隐式转换为 std 的 boost::vector::vector).我只是想知道这种行为是否适用于元组.

The same documentation page says that *a should produce something with the type vector<A>. This seems to be producing a std::vector<A> (or some kind of boost::vector<A> that can implicitly convert to a std::vector<A>). I just wanted to know if this same behavior was available for tuples.

推荐答案

简短回答:

使用#include .

更完整的答案:

如您所见 这里:

在属性表中,我们将仅使用 vectortuple 作为占位符.vector 的符号代表任何包含类型 A 元素的 STL 容器,符号 tuple 代表任何 Boost.Fusion包含 A、B、... 等元素的序列.最后,Unused 代表unused_type.

In the attribute tables, we will use vector<A> and tuple<A, B...> as placeholders only. The notation of vector<A> stands for any STL container holding elements of type A and the notation tuple<A, B...> stands for any Boost.Fusion sequence holding A, B, ... etc. elements. Finally, Unused stands for unused_type.

因此,当解析器/生成器具有 tuple<A,B...> 属性时,您可以使用任何融合序列(例如 fusion::vector 或 fusion::list)或任何可以适应融合序列的东西(例如 boost::array、boost::tuple、std::pair、std::tuple,您自己的使用 BOOST_FUSION_ADAPT_STRUCT 的结构).

So when a parser/generator has an attribute of tuple<A,B...> you can use any fusion sequence (such as fusion::vector or fusion::list) or anything that can be adapted to a fusion sequence (such as boost::array, boost::tuple, std::pair, std::tuple, your own struct using BOOST_FUSION_ADAPT_STRUCT).

当它有 vector 时,你可以使用 std::vector、std::list 甚至 std::map,如果你的元素是成对的.如果您还专门化了几个自定义点(至少是 boost::spirit::traits 中的 is_container、container_value 和 push_back_container),您也可以使用自己的结构.

And when it has vector<A> you can use std::vector, std::list, and even std::map if your elements are pairs. You can also use your own struct if you also specialize several customization points (at least is_container, container_value and push_back_container in boost::spirit::traits).

std::pair
为了能够将 std::pair 与精神一起使用,您只需要添加一个标题:

std::pair
In order to be able to use std::pair with spirit you just need to add a single header:

#include <boost/fusion/include/std_pair.hpp>
...
qi::rule<Iterator,std::pair<int,double>()> rule = 
    qi::int_ >> qi::lit(',') >> qi::double_;

std::tuple
从 boost 1.48.0 开始,您可以对 std::tuple 执行相同的操作:

std::tuple
Starting with boost 1.48.0 you can do the same for std::tuple:

#include <boost/fusion/adapted/std_tuple.hpp> 
...
qi::rule<Iterator,std::tuple<int,std::string,double>()> rule =
    qi::int_ >> qi::lit(',') >> +~qi::char_(',') >> qi::lit(',') >> qi::double_;

您自己的结构
在 BOOST_FUSION_ADAPT_STRUCT 的帮助下,您可以非常轻松地调整您的自定义结构:

Your own struct
You can adapt your custom struct very easily with the help of BOOST_FUSION_ADAPT_STRUCT:

#include <boost/fusion/include/adapt_struct.hpp>
...
struct normal_struct
{
    int integer;
    double real;
};

BOOST_FUSION_ADAPT_STRUCT(
    normal_struct,
    (int, integer)
    (double, real)
)
...
qi::rule<Iterator,normal_struct()> rule =
    qi::int_ >> qi::lit(',') >> qi::double_;

但是有一个已知的限制,当您尝试使用具有单个元素的结构时,该结构也是容器编译失败,除非您添加 qi::eps >>... 到您的规则.

There is one known limitation though, when you try to use a struct that has a single element that is also a container compilation fails unless you add qi::eps >> ... to your rule.

struct struct_with_single_element_container
{
    std::vector<int> cont;
};

BOOST_FUSION_ADAPT_STRUCT(
    struct_with_single_element_container,
    (std::vector<int>, cont)
)
...
qi::rule<Iterator,struct_with_single_element_container()> rule =
    qi::eps >> qi::int_%qi::lit(',');

std::map
您可以简单地使用 std::map 作为 std::pairs 的容器.但请记住,如果您的输入中有重复的键,则只有第一个会被插入到地图中(如果您使用 multimap,当然一切都会被插入):

std::map
You can simply use std::map as a container of std::pairs. Keep in mind though that if there are repeated keys in your input, only the first one will be inserted to the map (if you use multimap everything will be inserted of course):

#include <boost/fusion/include/std_pair.hpp>
...
qi::rule<std::string::const_iterator, std::pair<double,int>()> pair_rule = 
    qi::double_ >> qi::lit('=') >> qi::int_;
qi::rule<std::string::const_iterator, std::map<double,int>()> rule = 
    pair_rule%qi::lit(',');
//You can also use
//qi::rule<std::string::const_iterator, std::map<double,int>()> rule =
    //(qi::double_ >> qi::lit('=') >> qi::int_)%qi::lit(',');

您自己的结构作为容器
使用精神的
定制点,您还可以制作您的结构在处理属性时表现得好像它是一个容器.您需要专门化的最低限度是 is_containercontainer_valuepush_back_container.下面是几个例子:

Your own struct as a container
Using spirit's customization points you can also make your struct behave as if it were a container when dealing with attributes. The minimum you need to specialize are is_container, container_value and push_back_container. Here are a couple of examples:

第一个相当简单(而且很傻).它使您的结构具有与 std::vector 兼容的属性.每次解析 int 时,它都会添加到累加器中的总数中.您可以在此处找到不那么愚蠢的方法和此处(在旧答案"中).

The first one is rather simple (and silly). It makes your struct have an attribute compatible with std::vector<int>. Every time an int is parsed it is added to the total in the accumulator. You can find less silly approaches here and here (in the "old answer").

struct accumulator
{
    accumulator(): total(){}
    int total;
};

namespace boost{ namespace spirit{ namespace traits
{
    template<>
    struct is_container<accumulator> : boost::mpl::true_
    {};

    template<>
    struct container_value<accumulator>
    {
        typedef int type;
    };

    template<>
    struct push_back_container<accumulator,int>
    {
        static bool call(accumulator& c, int val)
        {
            c.total+=val;
            return true;
        }
    };
}}}
...
qi::rule<Iterator,accumulator()> rule =
    qi::int_%qi::lit(',');

<小时>

第二个稍微复杂一点(不多).它使您的结构具有与 std::vector<boost::variant<int,std::string> 兼容的属性.>.当一个 int 被解析时,它被添加到分发器中的 ints 容器中,类似的字符串存储在 strings 容器中.使用此示例(123).


The second one is a little more complex (not much). It makes your struct have an attribute compatible with std::vector<boost::variant<int,std::string> >. When an int is parsed it is added to the ints container in the distributor, similarly strings are stored in the strings container. Examples using this (1, 2 and 3).

struct distributor
{
    distributor():ints(),strings(){}
    std::vector<int> ints;
    std::vector<std::string> strings;
};

namespace boost{ namespace spirit{ namespace traits
{
    template<>
    struct is_container<distributor> : boost::mpl::true_
    {};

    template<>
    struct container_value<distributor>
    {
        typedef boost::variant<int,std::string> type;
    };

    template<>
    struct push_back_container<distributor,int>
    {
        static bool call(distributor& c, int val)
        {
            c.ints.push_back(val);
            return true;
        }
    };

    template<>
    struct push_back_container<distributor,std::string>
    {
        static bool call(distributor& c, std::string const& val)
        {
            c.strings.push_back(val);
            return true;
        }
    };
}}}
...
qi::rule<std::string::const_iterator, std::string()> string_rule = 
    +~qi::char_(',');
qi::rule<std::string::const_iterator, distributor()> rule = 
    (qi::int_ | string_rule)%qi::lit(',');

<小时>

单个 cpp 文件中的所有测试

#include <iostream>
#include <string>
#include <utility>
#include <tuple>
#include <list>
#include <vector>
#include <map>

#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted/std_tuple.hpp> 

#include <boost/spirit/include/qi.hpp>

#include <boost/variant.hpp>

namespace qi=boost::spirit::qi;

struct normal_struct
{
    int integer;
    double real;
};

struct struct_with_single_element_container
{
    std::vector<int> cont;
};

BOOST_FUSION_ADAPT_STRUCT(
    normal_struct,
    (int, integer)
    (double, real)
)

BOOST_FUSION_ADAPT_STRUCT(
    struct_with_single_element_container,
    (std::vector<int>, cont)
)

struct accumulator
{
    accumulator(): total(){}
    int total;
};

namespace boost{ namespace spirit{ namespace traits
{
    template<>
    struct is_container<accumulator> : boost::mpl::true_
    {};

    template<>
    struct container_value<accumulator>
    {
        typedef int type;
    };

    template<>
    struct push_back_container<accumulator,int>
    {
        static bool call(accumulator& c, int val)
        {
            c.total+=val;
            return true;
        }
    };
}}}

struct distributor
{
    distributor():ints(),strings(){}
    std::vector<int> ints;
    std::vector<std::string> strings;
};

namespace boost{ namespace spirit{ namespace traits
{
    template<>
    struct is_container<distributor> : boost::mpl::true_
    {};

    template<>
    struct container_value<distributor>
    {
        typedef boost::variant<int,std::string> type;
    };

    template<>
    struct push_back_container<distributor,int>
    {
        static bool call(distributor& c, int val)
        {
            c.ints.push_back(val);
            return true;
        }
    };

    template<>
    struct push_back_container<distributor,std::string>
    {
        static bool call(distributor& c, std::string const& val)
        {
            c.strings.push_back(val);
            return true;
        }
    };
}}}


int main()
{
    {
        std::pair<int,double> parsed;
        qi::rule<std::string::const_iterator, std::pair<int,double>()> rule = 
                    qi::int_ >> qi::lit(',') >> qi::double_;
        std::string test="1,2.5";
        std::string::const_iterator iter=test.begin(), end=test.end();
        bool result = qi::parse(iter,end,rule,parsed);
        if(result && iter==end)
        {
            std::cout << "Success." << std::endl;
            std::cout << "First: " << parsed.first << ", Second: " << parsed.second << std::endl;
        }
        else
        {
            std::cout << "Failure." << std::endl;
            std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
        }
    }

    {
        std::tuple<int,std::string,double> parsed;
        qi::rule<std::string::const_iterator, std::tuple<int,std::string,double>()> rule = 
                    qi::int_ >> qi::lit(',') >> +~qi::char_(',') >> qi::lit(',') >> qi::double_;
        std::string test="1,abc,2.5";
        std::string::const_iterator iter=test.begin(), end=test.end();
        bool result = qi::parse(iter,end,rule,parsed);
        if(result && iter==end)
        {
            std::cout << "Success." << std::endl;
            std::cout << "get<0>: " << std::get<0>(parsed) << ", get<1>: " << std::get<1>(parsed) << ", get<2>: " << std::get<2>(parsed) << std::endl;
        }
        else
        {
            std::cout << "Failure." << std::endl;
            std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
        }
    }

    {
        normal_struct parsed;
        qi::rule<std::string::const_iterator, normal_struct()> rule = 
                    qi::int_ >> qi::lit(',') >> qi::double_;
        std::string test="1,2.5";
        std::string::const_iterator iter=test.begin(), end=test.end();
        bool result = qi::parse(iter,end,rule,parsed);
        if(result && iter==end)
        {
            std::cout << "Success." << std::endl;
            std::cout << "integer: " << parsed.integer << ", real: " << parsed.real << std::endl;
        }
        else
        {
            std::cout << "Failure." << std::endl;
            std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
        }
    }

    {
        struct_with_single_element_container parsed;
        //there is a problem when you have a struct with a single element container, the workaround is simply adding qi::eps to the rule
        qi::rule<std::string::const_iterator, struct_with_single_element_container()> rule = 
                    qi::eps >> qi::int_%qi::lit(','); 
        std::string test="1,2";
        std::string::const_iterator iter=test.begin(), end=test.end();
        bool result = qi::parse(iter,end,rule,parsed);
        if(result && iter==end)
        {
            std::cout << "Success." << std::endl;
            std::cout << "[0]: " << parsed.cont[0] << ", [1]: " << parsed.cont[1] << std::endl;
        }
        else
        {
            std::cout << "Failure." << std::endl;
            std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
        }
    }

    {
        std::list<int> parsed;
        qi::rule<std::string::const_iterator, std::list<int>()> rule = 
                    qi::int_%qi::lit(',');
        std::string test="1,2";
        std::string::const_iterator iter=test.begin(), end=test.end();
        bool result = qi::parse(iter,end,rule,parsed);
        if(result && iter==end)
        {
            std::cout << "Success." << std::endl;
            std::cout << "front: " << parsed.front() << ", back: " << parsed.back() << std::endl;
        }
        else
        {
            std::cout << "Failure." << std::endl;
            std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
        }
    }

    {
        std::map<double,int> parsed;
        qi::rule<std::string::const_iterator, std::pair<double,int>()> pair_rule = 
                    qi::double_ >> qi::lit('=') >> qi::int_;
        qi::rule<std::string::const_iterator, std::map<double,int>()> rule = 
                    pair_rule%qi::lit(',');
        std::string test="2.5=1,3.5=2";
        std::string::const_iterator iter=test.begin(), end=test.end();
        bool result = qi::parse(iter,end,rule,parsed);
        if(result && iter==end)
        {
            std::cout << "Success." << std::endl;
            std::cout << "map[2.5]: " << parsed[2.5] << ", map[3.5]: " << parsed[3.5] << std::endl;
        }
        else
        {
            std::cout << "Failure." << std::endl;
            std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
        }
    }

    {
        accumulator parsed;
        qi::rule<std::string::const_iterator, accumulator()> rule = 
                    qi::int_%qi::lit(',');
        std::string test="1,2,3";
        std::string::const_iterator iter=test.begin(), end=test.end();
        bool result = qi::parse(iter,end,rule,parsed);
        if(result && iter==end)
        {
            std::cout << "Success." << std::endl;
            std::cout << "total: " << parsed.total << std::endl;
        }
        else
        {
            std::cout << "Failure." << std::endl;
            std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
        }
    }

    {
        distributor parsed;
        qi::rule<std::string::const_iterator, std::string()> string_rule = 
                    +~qi::char_(',');
        qi::rule<std::string::const_iterator, distributor()> rule = 
                    (qi::int_ | string_rule)%qi::lit(',');
        std::string test="abc,1,2,def,ghi,3,jkl";
        std::string::const_iterator iter=test.begin(), end=test.end();
        bool result = qi::parse(iter,end,rule,parsed);
        if(result && iter==end)
        {
            std::cout << "Success." << std::endl;
            std::cout << "ints" << std::endl;
            for(auto val: parsed.ints)
                std::cout << val << std::endl;
            std::cout << "strings" << std::endl;
            for(const auto& val: parsed.strings)
                std::cout << val << std::endl;
        }
        else
        {
            std::cout << "Failure." << std::endl;
            std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
        }
    }

}

这篇关于让 boost::spirit::qi 使用 stl 容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆