boost :: spirit :: qi和boost :: phoenix :: push_back

时间:2017-12-01 15:20:38

标签: c++ boost boost-spirit

根据Using boost::spirit::qi and boost::phoenix::push_back上的质量检查,以下代码运行良好 - 使用C ++ 14编译。

#include <string>
#include <vector>
#include <iostream>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace qi = boost::spirit::qi;

typedef std::vector<unsigned int> uint_vector_t;

std::ostream& operator<<(std::ostream& out, const uint_vector_t &data)
{
    for (unsigned int i(0); i < data.size(); i++)
    {
        out << data[i] << '\n';
    }
    return out;
}

struct MyStruct
{
    uint_vector_t m_aList;
    uint_vector_t m_bList;
};


template<typename Iterator, typename Skipper>
struct MyParser : public boost::spirit::qi::grammar<Iterator,
        MyStruct(),Skipper>
{
    MyParser() :
        MyParser::base_type(Parser, "Parser")
    {
        using boost::spirit::qi::uint_;
        using boost::spirit::qi::_val;
        using boost::spirit::qi::_1;

        using boost::phoenix::at_c;
        using boost::phoenix::push_back;
        using boost::phoenix::bind;

        aParser = "a=" >> uint_;
        bParser = "b=" >> uint_;

        Parser =
           *(  aParser [push_back(bind(&MyStruct::m_aList, _val), _1)]
            |  bParser [push_back(bind(&MyStruct::m_bList, _val), _1)]
            );
    }
    boost::spirit::qi::rule<Iterator, MyStruct(), Skipper> Parser;
    boost::spirit::qi::rule<Iterator, unsigned int(), Skipper> aParser, bParser;
};

int main()
{
    using boost::spirit::qi::phrase_parse;

    std::string input("a=0\nb=7531\na=2\na=3\nb=246\n");
    std::string::const_iterator begin = input.begin();
    std::string::const_iterator end = input.end();
    MyParser<std::string::const_iterator, qi::space_type> parser;

    MyStruct result;
    bool succes = qi::phrase_parse(begin, end, parser,qi::space,result);
    assert(succes);

    std::cout << "===A===\n" <<result.m_aList << "===B===\n" << result.m_bList << std::endl;
    return 0;
}

结果是:

===A===
0
2
3
===B===
7531
246

在结构中添加另一个qi :: symbols元素,新添加的元素应该被解析为FRUIT :: APPLE(0),但它实际上是 indeterminate (随机出现)。 / p>

#include <string>
#include <vector>
#include <iostream>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace qi = boost::spirit::qi;

typedef std::vector<unsigned int> uint_vector_t;

std::ostream& operator<<(std::ostream& out, const uint_vector_t &data)
{
    for (unsigned int i(0); i < data.size(); i++)
    {
        out << data[i] << '\n';
    }
    return out;
}

struct MyStruct
{
    enum class  FRUIT
    {
        APPLE,
        BANANA,
        PEAR,
    } fruit;
    uint_vector_t m_aList;
    uint_vector_t m_bList;
};

BOOST_FUSION_ADAPT_STRUCT
(
    MyStruct,
    (MyStruct::FRUIT, fruit)
    (uint_vector_t, m_aList)
    (uint_vector_t, m_bList)
)


template<typename Iterator, typename Skipper>
struct MyParser : public boost::spirit::qi::grammar<Iterator,
        MyStruct(),Skipper>
{
    MyParser() :
        MyParser::base_type(Parser, "Parser")
    {
        using boost::spirit::qi::uint_;
        using boost::spirit::qi::_val;
            using boost::spirit::qi::_1;

        using boost::phoenix::at_c;
        using boost::phoenix::push_back;
        using boost::phoenix::bind;

        fruiter.add
                ("apple",   MyStruct::FRUIT::APPLE)
                ("banana",  MyStruct::FRUIT::BANANA)
                ("pear",    MyStruct::FRUIT::PEAR)
                ;

        aParser = "a=" >> uint_;
        bParser = "b=" >> uint_;

        Parser = fruiter >>
           *(  aParser [push_back(bind(&MyStruct::m_aList, _val), _1)]
            |  bParser [push_back(bind(&MyStruct::m_bList, _val), _1)]
            );
    }
    boost::spirit::qi::rule<Iterator, MyStruct(), Skipper> Parser;
    boost::spirit::qi::rule<Iterator, unsigned int(), Skipper> aParser, bParser;
    boost::spirit::qi::symbols<char, MyStruct::FRUIT>                   fruiter;
};

int main()
{
    using boost::spirit::qi::phrase_parse;

    std::string input("apple\na=0\nb=7531\na=2\na=3\nb=246\n");
    std::string::const_iterator begin = input.begin();
    std::string::const_iterator end = input.end();
    MyParser<std::string::const_iterator, qi::space_type> parser;

    MyStruct result;
    bool succes = qi::phrase_parse(begin, end, parser,qi::space,result);
    assert(succes);

    std::cout << "Fruit: " << int(result.fruit) << "\n===A===\n" <<result.m_aList << "===B===\n" << result.m_bList << std::endl;
    return 0;
}

生成的qi :: symbols元素是随机的而不是0.示例输出类似于

Fruit: 29899839
===A===
0
2
3
===B===
7531
246

然而,qi :: symbols元素本身也可以正常工作。

#include <string>
#include <vector>
#include <iostream>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace qi = boost::spirit::qi;


struct MyStruct
{
    enum class  FRUIT
    {
        APPLE,
        BANANA,
        PEAR,
    } fruit;
};

BOOST_FUSION_ADAPT_STRUCT
(
    MyStruct,
    (MyStruct::FRUIT, fruit)
)


template<typename Iterator, typename Skipper>
struct MyParser : public boost::spirit::qi::grammar<Iterator,
        MyStruct(),Skipper>
{
    MyParser() :
        MyParser::base_type(Parser, "Parser")
    {
        using boost::spirit::qi::uint_;
        using boost::spirit::qi::_val;
            using boost::spirit::qi::_1;

        using boost::phoenix::at_c;
        using boost::phoenix::push_back;
        using boost::phoenix::bind;

        fruiter.add
                ("apple",   MyStruct::FRUIT::APPLE)
                ("banana",  MyStruct::FRUIT::BANANA)
                ("pear",    MyStruct::FRUIT::PEAR)
                ;

        Parser = fruiter;
    }
    boost::spirit::qi::rule<Iterator, MyStruct(), Skipper> Parser;
    boost::spirit::qi::symbols<char, MyStruct::FRUIT>                   fruiter;
};

int main()
{
    using boost::spirit::qi::phrase_parse;

    std::string input("apple");
    std::string::const_iterator begin = input.begin();
    std::string::const_iterator end = input.end();
    MyParser<std::string::const_iterator, qi::space_type> parser;

    MyStruct result;
    bool succes = qi::phrase_parse(begin, end, parser,qi::space,result);
    assert(succes);

    std::cout << "Fruit: " << int(result.fruit) << "\n";
    return 0;
}

结果如下:

Fruit: 0

我做错了什么?提前谢谢。

1 个答案:

答案 0 :(得分:2)

语义动作会禁止属性的自动传播。这显然也是该程序的第一个版本没有适应MyResult结构的原因。

所以要么

  1. 坚持语义行为¹(删除adapt-struct)

    <强> Live On Coliru

        Parser = fruiter[bind(&MyStruct::fruit, _val) = _1] >> 
            *( aParser [push_back(bind(&MyStruct::m_aList, _val), _1)] 
            |  bParser [push_back(bind(&MyStruct::m_bList, _val), _1)]
            );
    
  2. 或使用operator%=重新启用自动属性传播语义。

    // NOTE c++11+ syntax:
    BOOST_FUSION_ADAPT_STRUCT(MyStruct, fruit, m_aList, m_bList)
    
    Parser %= fruiter >> 
        *( aParser [push_back(bind(&MyStruct::m_aList, _val), _1)] 
        |  bParser [push_back(bind(&MyStruct::m_bList, _val), _1)]
        );
    

    请注意,如果fruit不是第一个适应的序列元素,这很容易就会崩溃。事实上,只有适应预期的元素才能更清晰:

    BOOST_FUSION_ADAPT_STRUCT(MyStruct, fruit)
    

    更清楚地明确指出哪些属性会传播:

    Parser %= fruiter >> 
        omit [
           *( aParser [push_back(bind(&MyStruct::m_aList, _val), _1)] 
            | bParser [push_back(bind(&MyStruct::m_bList, _val), _1)]
            )
        ];
    

    <强> Live On Coliru

  3. 完整列表

    <强> Live On Coliru

    #include <iostream>
    #include <string>
    #include <vector>
    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/spirit/include/qi.hpp>
    namespace qi = boost::spirit::qi;
    
    typedef std::vector<unsigned int> Uints;
    
    namespace std {
        std::ostream& operator<<(std::ostream& out, const Uints &data) {
            for (auto i : data) out << i << " ";
            return out << "\n";
        }
    }
    
    struct MyStruct {
        enum class FRUIT {
            APPLE,
            BANANA,
            PEAR,
        } fruit;
    
        friend std::ostream& operator<<(std::ostream& out, FRUIT f) {
            switch(f) {
                case FRUIT::APPLE:  return out << "APPLE";
                case FRUIT::BANANA: return out << "BANANA";
                case FRUIT::PEAR:   return out << "PEAR";
            }
            return out << "FRUIT[?" << static_cast<int>(f) << "]";
        }
        Uints m_aList;
        Uints m_bList;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(MyStruct, fruit)
    
    template <typename Iterator, typename Skipper>
    struct MyParser : public qi::grammar<Iterator, MyStruct(), Skipper> {
        MyParser() : MyParser::base_type(Parser, "Parser") {
            using namespace qi;
            using boost::phoenix::push_back;
            using boost::phoenix::bind;
    
            fruiter.add("apple", MyStruct::FRUIT::APPLE)("banana", MyStruct::FRUIT::BANANA)("pear", MyStruct::FRUIT::PEAR);
    
            aParser = "a=" >> uint_;
            bParser = "b=" >> uint_;
    
            Parser %= fruiter >> 
                omit [
                   *( aParser [push_back(bind(&MyStruct::m_aList, _val), _1)] 
                    | bParser [push_back(bind(&MyStruct::m_bList, _val), _1)]
                    )
                ];
        }
      private:
        qi::rule<Iterator, MyStruct(), Skipper> Parser;
        qi::rule<Iterator, unsigned int(), Skipper> aParser, bParser;
        qi::symbols<char, MyStruct::FRUIT> fruiter;
    };
    
    int main() {
        std::string input("banana\na=0\nb=7531\na=2\na=3\nb=246\n");
        using It = std::string::const_iterator;
        It begin = input.begin(), end = input.end();
        MyParser<It, qi::space_type> parser;
    
        MyStruct result;
        bool succes = qi::phrase_parse(begin, end, parser, qi::space, result);
    
        if (succes) {
            std::cout 
                << "Fruit: " << result.fruit 
                << "\n===A===\n" <<result.m_aList << "===B===\n" << result.m_bList << std::endl;
        } else {
            std::cout << "Parse failed\n";
        }
    }
    

    打印

    Fruit: BANANA
    ===A===
    0 2 3 
    ===B===
    7531 246 
    

    ¹重复我的口头禅:Boost Spirit: "Semantic actions are evil"?