【问题标题】:Boost Spirit : how to convert basic types?Boost Spirit:如何转换基本类型?
【发布时间】:2012-11-22 15:14:40
【问题描述】:

假设我有一个std::string 属性,但为了便于解析,我想使用qi::int_qi::double_

有没有一种简单的方法可以将转换作为语义操作进行

我尝试过这样的事情:

 std::stringstream ss; 
 my_int_as_str = qi::int_ [ ref(ss)<<_1; _val=ss.str() ];

但这甚至无法编译。

编辑 - 尝试使用下面的 sehe 答案

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

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

namespace qi=boost::spirit::qi;
namespace phx=boost::phoenix;

int main(int argc, char* argv[])
{
    std::string test="123";
    std::string result;

    // 1. qi::raw[ qi::int_ ]       works
    // 2. qi::lexeme[ qi::int_ ]    doesn't
    // 3. qi::as_string[ qi::int_ ] doesn't
    qi::rule<std::string::const_iterator, std::string()> my_int_as_str = 
                   qi::raw[ qi::int_ ];
    parse( test.cbegin(), test.cend(), my_int_as_str, result );

    std::cout << result << std::endl;

    // -------------------------------------------------------------------------

    std::string test_vector="456 789";
    std::vector<std::string> result_vector;

    // 4. qi::raw[ qi::int_ ]       won't compile
    // 5. qi::lexeme[ qi::int_ ]    won't compile
    // 6. qi::as_string[ qi::int_ ] doesn't
    qi::rule<std::string::const_iterator,std::vector<std::string>(),qi::space_type> 
            my_int_as_str_vector = qi::lexeme[ qi::int_ ];

    phrase_parse(test_vector.cbegin(),test_vector.cend(),
            my_int_as_str_vector,qi::space,result_vector);

    for(auto& string: result_vector)
        std::cout << string << std::endl;

    return 0;
}

【问题讨论】:

    标签: c++ boost-spirit


    【解决方案1】:

    您可以通过将transform_attribute 专门用于您的类型来使用attr_cast

    #include <iostream>
    #include <string>
    #include <vector>
    
    #include <boost/spirit/include/qi.hpp>
    
    namespace qi=boost::spirit::qi;
    namespace phx=boost::phoenix;
    
    
    namespace boost { namespace spirit { namespace traits
    {
    
        template <>
        struct transform_attribute<std::string, int, qi::domain>
        {
            typedef int type;
            static int pre(std::string& d) { return 0; }//not useful in this case but required to avoid compiler errors
            static void post(std::string& val, int const& attr) //`val` is the "returned" string, `attr` is what int_ parses
            {
                std::stringstream ss;
                ss << attr;
                val= ss.str();
            }
            static void fail(std::string&) {}
        };
    }}}
    
    int main(int argc, char* argv[])
    {
        std::string test="123";
        std::string test_vector="456 789";
    
        qi::rule<std::string::const_iterator,std::string()> my_int_as_str = qi::attr_cast(qi::int_);
        qi::rule<std::string::const_iterator,std::vector<std::string>(),qi::space_type> my_int_as_str_vector= *qi::attr_cast(qi::int_);
    
        std::string result;
        std::vector<std::string> result_vector;
    
        parse(test.cbegin(),test.cend(),my_int_as_str,result);
        phrase_parse(test_vector.cbegin(),test_vector.cend(),my_int_as_str_vector,qi::space,result_vector);
    
    
        std::cout << result << std::endl;
        for(auto& string: result_vector)
            std::cout << string << std::endl;
    
    
        return 0;
    }
    

    如果您真的需要/想要使用semantic actions,最简单的替代方法是定义一个以 int 作为参数并返回字符串的函数(这比attr_cast 替代方法更简单,但速度也较慢,几乎是两倍我非常简单的基准测试中的时间):

    std::string semantic_transform(int i)
    {
        std::stringstream ss;
        ss<<i;
        return ss.str();
    }
    ...
    std::string string_semantic;
    qi::parse(test.cbegin(),test.cend(),qi::int_[&semantic_transform],string_semantic);
    std::cout << string_semantic << std::endl;
    

    【讨论】:

    • 优秀 - tyvm 的详细答案(基准评论也很高兴知道)
    • +1 获取大量优秀信息和技术。但是,对于这个特定的任务,我会更喜欢我自己的答案:)
    【解决方案2】:

    内置方式:

    qi::rule<Iterator, std::string()> my_int_as_str = 
           qi::as_string [ qi::int_ ];
    

    对于这种特殊的简单情况,qi::rawqi::lexeme 会同样有效,因为它们都公开了源迭代器对,它们具有内置的 assignment-to-attribute 用于 std::string

    详细说明:

    my_int_as_str = qi::lexeme [ qi::int_ ]; // equivalent
    my_int_as_str = qi::raw [ qi::int_ ];    // idem
    

    【讨论】:

    • +1 哇 - 这更好 - 你能用你的 qi::rawqi::lexeme 评论的例子详细说明吗?我会试试你的例子 - ty.
    • ty - 我通过 (std::string,std::vector<:string>) 尝试了 6 种方式(as_string,lexeme,raw),而我唯一可以使用的方式是 raw标准::字符串。三者之间一定有一些细微的区别(代码在 OP 编辑​​中)...我会尝试谷歌一些文档。
    • +1 使用 qi::raw。它更简单且无限快(使用 msvc11 快 40 倍以上,使用 g++ 4.7.1 快约 8 倍)。其他选项与 my_int_as_str=qi::int_ 将 int 转换为 char ({) 相同。
    • 显然大部分差异是由于使用了 stringstream(interesting blog about it)。如果我将post 函数的内容更改为karma::generate(std::back_inserter(val),karma::int_,attr);,则时间是相当的(仍然比qi::raw 慢一点,但比原来的要快得多)。
    • @llonesmiz 请参阅Do C++ formatting libraries generally fall back to *sprintf for numeric formatting? 了解 Karma 与 sstream 的性能。当然qi::raw 应该是最快的,因为它没有转换。如果您可以改用+qi::digit,它会更快。为了获得出色的性能,您可以只返回 boost::iterator_pair&lt;Iterator&gt; 并延迟字符串的构造(假设是前向迭代器,而不是输入迭代器)。这基本上就是 Spirit Lex 对令牌所做的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多