Search code examples
c++parsingboosttype-conversionboost-spirit-qi

How can I convert from qi::double_ to string?


I am using spiri::qi to parse a text and pushing what I parse into a vector<string>, for the most part its fine since they are mostly names and addresses, but there are also some numbers that I am parsing with double_, but once I push it to the vector it considers it a character code, like '\x3' in lieu of 3.0. I don’t want to make use of variant since its too much work for just a few cases. Is there anyway I can convert the result of double_ to string before pushing it?


Solution

  • Use raw[double_] (or even more exactly as_string[raw[double_]]).

    The first works like any rule that has attribute compatibility with a container of characters. The latter atomically exposes a std::string (there's one for std::wstring as well).

    BONUS

    To complete Jonathan Mee's suggestion to "just use the language" (paraphrasing... :)) you can, see the last demo grammar below:

    Live On Coliru

    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    
    namespace qi = boost::spirit::qi;
    namespace px = boost::phoenix;
    
    int main() {
    
        using It = std::string::const_iterator;
        auto to_string_f = [](auto v) { return std::to_string(v); };
        px::function<decltype(to_string_f)> to_string_ { to_string_f };
    
        for (std::string const input : { "3.14", "+inf", "NaN", "-INF", "99e-3" })
        {
            for (auto const& grammar : std::vector<qi::rule<It, std::string()>> {
                    //qi::double_,  // results in strange binary interpretations, indeed
                    qi::raw[ qi::double_ ], 
                    qi::as_string [ qi::raw[ qi::double_ ] ], 
                    qi::double_ [ qi::_val = to_string_(qi::_1) ], 
                })
            {
                auto f = input.begin(), l = input.end();
                std::string result;
                if (qi::parse(f, l, grammar, result))
                    std::cout << input << "\t->\t" << result << "\n";
                else
                    std::cout << "FAILED for '" << input << "'\n";
            }
        }
    }
    

    Printing

    3.14    ->  3.14
    3.14    ->  3.14
    3.14    ->  3.140000
    +inf    ->  +inf
    +inf    ->  +inf
    +inf    ->  inf
    NaN ->  NaN
    NaN ->  NaN
    NaN ->  nan
    -INF    ->  -INF
    -INF    ->  -INF
    -INF    ->  -inf
    99e-3   ->  99e-3
    99e-3   ->  99e-3
    99e-3   ->  0.099000
    

    Note that std::to_string doesn't result in the literal input, so it might not roundtrip faithfully for your purposes.