boost - boost boost::spirit::qi以使用STL容器

标签 boost boost-spirit boost-spirit-qi

我正在尝试使用boost.spirit的qi库解析某些内容,而我遇到了一个问题。根据spirit docsa >> b应该产生类型为tuple<A, B>的东西。但这是boost::tuple(又名 fusion 矢量),而不是std::tuple(我想要的)。

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

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

最佳答案

简短答案:

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

更多完整的答案:

如您所见here:

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 序列(例如fusion::vector或fusion::list)或任何可以适应 fusion 序列的东西(例如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
为了能够在 spirit 上使用std::pair,您只需要添加一个 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执行相同的操作:
#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轻松调整自定义结构:
#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 >> ...,否则容器编译也会失败。
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的容器。但是请记住,如果输入中有重复的键,则只会将第一个键插入到 map 中(如果使用多图,则当然会插入所有内容):
#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(',');

您自己的结构作为容器
使用Spirit的customization points,还可以使您的结构在处理属性时像容器一样工作。您需要专门研究的最低要求是is_containercontainer_valuepush_back_container。以下是几个示例:

第一个很简单(而且很愚蠢)。它使您的结构具有与std::vector<int>兼容的属性。每次解析一个int都会将其添加到累加器的总数中。您可以发现herehere少一些愚蠢的方法(在“旧答案”中)。
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)。
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 - boost boost::spirit::qi以使用STL容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18158376/

相关文章:

c++ - qi::rule 以继承属性作为继承属性

c++ - Boost shared_ptr 不会立即销毁对象

c++ - UDP端口的低延迟读取

c++ - 无法根据 boost::spirit::qi 中的规则创建语法

c++ - boost::spirit:多维输入的迭代器和解析器

c++ - spirit::qi :将继承的属性引用传递给 phoenix::function

python - 在 mac 雪豹 macports 上 boost python 导入错误

C++ 通用回调实现

c++ - 使用 boost spirit 解析带有可选分隔符的字符串

c++ - 如何使用 parse/phrase_parse 函数