为表达式的语义动作设置属性,传递给qi :: phrase_parse

时间:2013-08-25 14:22:31

标签: c++ boost-spirit boost-spirit-qi

在解析过程中,我只需要在语义操作中设置一些属性(因为它们来自正在解析的数据,我想避免global变量和对BOOST_FUSION_ADAPT_STRUCT的依赖以及我的代码应该是通用的,以便我可以将它重用于多种类型)。如果我使用多个传递给qi::phrase_parse的变量,我会得到很长的编译错误列表。我非常需要帮助: - )

#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3

#include <boost/function.hpp>
#include <boost/phoenix/fusion.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>

#include <iostream>
#include <string>
#include <climits>

namespace qi = boost::spirit::qi;
namespace ph = boost::phoenix;
namespace ascii = boost::spirit::ascii;

int main( int argc, char**argv )
{
    bool rc;
    std::string input("");

    //Test case 1 works fine
    {
        auto iter( input.begin() );
        auto last( input.end() );
        int val1=33;
        rc = qi::phrase_parse( iter, last, qi::eps[ qi::_val=11 ] , 
                   ascii::space, val1 ) && iter==last;
        if( rc )
            std::cout << "val1=" << val1 << std::endl; 
    }
    //Test case 2 does not compile
    {
        auto iter( input.begin() );
        auto last( input.end() );
        int val1=33;
        int val2=0;
        rc = qi::phrase_parse( iter, last, 
                qi::eps[ ph::at_c<0>(qi::_val)=1,ph::at_c<1>(qi::_val)=2 ],
                ascii::space, val1,val2 ) && iter==last;
        if( rc )
            std::cout << "val1=" << val1 << 
                         " val2=" << val2 << std::endl;         
    }
    //Test case 3 works fine
    {
        auto iter( input.begin() );
        auto last( input.end() );
        int val1=33;
        int val2=0;
        rc = qi::phrase_parse( iter, last,
                qi::attr(1)>>qi::attr(2),
                ascii::space, val1,val2 ) && iter==last;
        if( rc )
            std::cout << "val1=" << val1 <<
                         " val2=" << val2 << std::endl;
    }

    return 0;
}

我从cv_and_he获取了“自定义”my_phrase_parse,但它打破了我想要运行的完整测试用例的编译:

template<typename T,typename R>
void testParser( R rule )
{
    for ( const auto input : std::vector< std::string >{ "5 1.0 2.0 3.0 4.0 5.0", "1 1.0", "0" , "", "2 3 ab" } )
    {
        bool rc;
        T maxValue;
        T minValue;
        auto iter( input.begin() );
        auto last( input.end() );
        std::vector< T > v;
        rc = my_phrase_parse( iter, last,
            qi::eps[
                     ph::at_c<0>(qi::_val)=std::numeric_limits<T>::max(),
                     ph::at_c<1>(qi::_val)=std::numeric_limits<T>::min()
                   ]
            >> -( qi::omit[ qi::int_] 
            >> *rule[ ph::if_(ph::at_c<0>(qi::_val)>qi::_1)[ ph::at_c<0>(qi::_val)=qi::_1 ],
                      ph::if_(ph::at_c<1>(qi::_val)<qi::_1)[ ph::at_c<1>(qi::_val)=qi::_1 ]
                    ] )
                ,ascii::space, minValue, maxValue,v ) && iter==last;
        std::cout << ( rc ? "ok :`" : "err:`" ) << input << "` -> ";
        if( rc )
        {
            std::cout << "min=" << minValue << " max=" << maxValue << "\t";
            std::copy( v.begin(), v.end(), std::ostream_iterator<T>( std::cout," " ));
        }
        else
            std::cout << *iter;
        std::cout << std::endl;
    }
}
...
    testParser<double>( qi::double_ );

3 个答案:

答案 0 :(得分:1)

Phoenix placeholders that Spirit uses允许您操纵规则上下文中的重要信息。当您在调用parsephrase_parse的表达式中直接使用它们时,没有规则,因此没有上下文。在版本Boost 1.47.0之前没有工作,并且为了使这种行为一致,修复被应用于这些函数的单个参数版本,但显然不适用于可变参数。

避免此问题的一种方法是创建一个规则,该规则的属性为fusion::vector对您在调用phrase_parse时使用的类型的引用。

编辑:删除了“my_phrase_parse”,因为我不确定它是否正确

#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3

#include <boost/function.hpp>
#include <boost/phoenix/fusion.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>

#include <iostream>
#include <string>

namespace qi = boost::spirit::qi;
namespace ph = boost::phoenix;
namespace ascii = boost::spirit::ascii;
namespace fusion = boost::fusion;

int main( int argc, char**argv )
{
    bool rc;
    std::string input("");

    //Test case works fine
    {
        auto iter( input.begin() );
        auto last( input.end() );
        int val1=33;
        rc = qi::phrase_parse( iter, last, qi::eps[ qi::_val=11 ] , 
                   ascii::space, val1 ) && iter==last;
        if( rc )
            std::cout << "val1=" << val1 << std::endl; 
    }
    //You can use a rule
    {
        auto iter( input.begin() );
        auto last( input.end() );
        int val1=33;
        int val2=0;

        qi::rule<decltype(iter),fusion::vector<int&, int&>(),ascii::space_type> parser=qi::eps[ ph::at_c<0>(qi::_val)=1,ph::at_c<1>(qi::_val)=2 ];

        rc = qi::phrase_parse( iter, last, 
                parser,
                ascii::space, val1,val2 ) && iter==last;
        if( rc )
            std::cout << "val1=" << val1 << 
                         " val2=" << val2 << std::endl;      
    }

    return 0;
}

编辑2:添加了另一种方法来解决您要在编辑中解决的问题

#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3

#include <boost/spirit/include/qi_core.hpp>
#include <boost/spirit/include/qi_omit.hpp>

#include <iostream>
#include <string>
#include <climits>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

template <typename T>
struct min_max_set
{
    min_max_set():min(std::numeric_limits<T>::max()),max(std::numeric_limits<T>::min()),set(){}
    T min;
    T max;
    std::vector<T> set;
};

namespace boost{ namespace spirit { namespace traits
{
    template <typename T>
    struct is_container<min_max_set<T>>
        : boost::mpl::true_
    {};

    template <typename T>
    struct container_value<min_max_set<T>>
    {
        typedef T type;
    };

    template <typename T>
    struct push_back_container<min_max_set<T>,T>
    {
        static bool call(min_max_set<T>& cont, const T& val)
        {
            if(cont.min>val)
                cont.min=val;
            if(cont.max<val)
                cont.max=val;
            cont.set.push_back(val);
            return true;
        }
    };

}}}

template<typename T,typename R>
void testParser( R rule )
{
    for ( const auto input : std::vector< std::string >{ "5 1.0 2.0 3.0 4.0 5.0", "1 1.0", "0" , "", "2 3 ab" } )
    {
        bool rc;

        auto iter( input.begin() );
        auto last( input.end() );
        min_max_set<T> accum;   

        rc = qi::phrase_parse( iter, last,
                qi::omit[qi::int_] >> *rule
                ,ascii::space, accum ) && iter==last;
        std::cout << ( rc ? "ok :`" : "err:`" ) << input << "` -> ";
        if( rc )
        {
            std::cout << "min=" << accum.min << " max=" << accum.max << "\t";
            std::copy( accum.set.begin(), accum.set.end(), std::ostream_iterator<T>( std::cout," " ));
        }
        else
            std::cout << *iter;
        std::cout << std::endl;
    }
}


int main( int argc, char**argv )
{
    testParser<double>(qi::double_);
    return 0;
}

答案 1 :(得分:1)

对于我最初的问题,这是workaround

#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3

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

#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
#include <climits>

namespace qi = boost::spirit::qi;
namespace ph = boost::phoenix;
namespace ascii = boost::spirit::ascii;

template<typename T,typename R>
void testParser( R rule )
{
    for ( const auto &input : std::vector< std::string >{ "5 1.0 2.0 3.0 4.0 5.0" } )
    {
        bool rc=false;
        T maxValue, minValue;
        auto iter( input.begin() ), last( input.end() );
        std::vector< T > data;

        qi::rule< decltype(iter),std::vector<T>( T&, T& ),qi::space_type > mrule;

        mrule %=
            qi::eps[ qi::_r1=std::numeric_limits<T>::max(),
                     qi::_r2=std::numeric_limits<T>::min() ] >>
             -( qi::omit[ qi::int_[ ph::reserve( qi::_val,qi::_1 ) ] ]
                >> *rule[ ph::if_(qi::_r1>qi::_1)[ qi::_r1=qi::_1 ],
                          ph::if_(qi::_r2<qi::_1)[ qi::_r2=qi::_1 ]
                        ] );

        rc = qi::phrase_parse( iter, last, mrule( ph::ref(minValue), ph::ref(maxValue) ), qi::space, data ) && iter==last;

    std::cout << ( rc ? "ok :`" : "err:`" ) << input << "` -> ";
    if( rc )
    {
        std::cout << "min=" << minValue << " max=" << maxValue << "\t";
        std::copy( data.begin(), data.end(), std::ostream_iterator<T>( std::cout," " ));
        }
        else
            std::cout << *iter;
        std::cout << std::endl;
    }
}

int main( int argc, char**argv )
{
    testParser<double>( qi::double_ );
    return 0;
}

答案 2 :(得分:0)

我希望我能正确看到问题,但即使这有点不同,你可以毫不费力地存储几个值:

struct data {
  int _a;
  int _b;
  data(int a, int b) : _a(a), _b(b) {};
};

构建它:

qi::eps[qi::_val = phx::construct<data>(1, 2)]