获取boost :: spirit :: qi以使用stl容器

Nea*_*l P 6 boost boost-spirit boost-spirit-qi

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

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

相同的文档页面说明*a应该生成类型的东西vector<A>.这似乎产生了一种std::vector<A>(或某种boost::vector<A>可以隐含地转换为a std::vector<A>).我只是想知道元组是否有相同的行为.

llo*_*miz 13

简短的回答:

使用#include <boost/fusion/adapted/std_tuple.hpp>.

更完整的答案:

正如你在这里看到的:

在属性表中,我们将仅使用vector<A>tuple<A, B...>作为占位符.的符号vector<A>代表类型A的任何STL容器保持元件和所述符号tuple<A, B...>代表保持A,B任何Boost.Fusion序列,...等的元件.最后,Unused代表unused_type.

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

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

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

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

std :: tuple
从boost 1.48.0开始,你可以对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_;
Run Code Online (Sandbox Code Playgroud)

您自己的结构
您可以在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_;
Run Code Online (Sandbox Code Playgroud)

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

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(',');
Run Code Online (Sandbox Code Playgroud)

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

#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(',');
Run Code Online (Sandbox Code Playgroud)

您自己的结构作为容器
使用spirit的自定义点,您还可以使结构在处理属性时表现得像是一个容器.你需要专业化的最小值是is_container,container_valuepush_back_container.以下是几个例子:


第一个是相当简单(和愚蠢).它使您的struct具有与之兼容的属性std::vector<int>.每次解析一个int时,它都会被添加到累加器中的总数中.你可以在这里这里找到不那么愚蠢的方法(在"旧答案"中).

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(',');
Run Code Online (Sandbox Code Playgroud)

第二个更复杂(不多).它使您的struct具有与之兼容的属性std::vector<boost::variant<int,std::string> >.当解析int时,它被添加到ints分发器中的容器中,类似地,字符串存储在strings容器中.使用此(实施例1,23).

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(',');
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)