How to parse a double from an array of characters using boost::spirit::qi::parse

Viewed 97

I can parse numbers from characters stored in std::string, std::vector or std::array. But I am not able to do so when the characters are in a buffer held in std::unique_ptr. I could copy the buffer into a string then to boost spirit but I want to avoid this copy

Here is my attempt:

#include<memory>
#include<array>
#include<iostream>
#include "boost/spirit/include/qi.hpp"


int main()
{
    const int max_size = 40;
    std::array<wchar_t, max_size> numeric1;
    std::wstring src = L"5178120.3663";
    std::wcsncpy(numeric1.data(), src.data(), max_size);

    double num = 0.0;
    boost::spirit::qi::parse(numeric1.begin(), numeric1.end(), num);
    std::cout.precision(15);
    std::cout << num << std::endl; // OK

    std::unique_ptr<wchar_t[]> numeric2(new wchar_t[max_size]);
    std::wcsncpy(numeric2.get(), src.data(), max_size);
    std::wcout << L"ok = " << std::wstring(numeric2.get()) << std::endl; // OK

    boost::spirit::qi::parse(numeric2.data(), max_size, num); // fails to compile 
    std::cout.precision(15);
    std::cout << num << std::endl;

    //  'boost::spirit::qi::parse': no matching overloaded function found

    return 0;
}

The fix:

boost::spirit::qi::parse(numeric2.get(), numeric2.get() + wcslen(numeric2.get()), num);

See Zalman's anwer

2 Answers

boost::spirit::qi::parse takes a begin and end iterator. You likely want something like:

boost::spirit::qi::parse(numeric2.get(), numeric2.get() + wcsnlen(numeric2.get(), max_size), num);

I.e. plain pointers work as iterators and you can form the end pointer by adding to the begin one.

Too many things are off with this:

  • unique_ptr doesn't have a data() member function
  • qi::parse doesn't take a size as the second parameter
  • you copy indeterminate values liberally to the array/unique_ptr<wchar_t[]>
  • worse, you even try to parse it. Spirit does not treat NUL characters as special, so you'd "seem" to parse correctly, only because you're ignoring unparsed trailing garbage
  • you're mixing wcout and cout which is unspecified

Here's a fix of most things:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <array>
#include <iostream>
#include <memory>

int main() {
    const int max_size = 40;
    std::wstring src = L"5178120.3663";
    const int actual_len = src.length() + 1; // include NUL character

    {
        std::array<wchar_t, max_size> numeric1;
        std::wcsncpy(numeric1.data(), src.data(), actual_len);

        double num = 0.0;
        boost::spirit::qi::parse(numeric1.begin(), numeric1.end(), num);
        std::wcout.precision(15);
        std::wcout << num << std::endl; // OK
    }

    {
        std::unique_ptr<wchar_t[]> numeric2(new wchar_t[max_size]);
        std::wcsncpy(numeric2.get(), src.data(), actual_len);

        double num = 0.0;
        boost::spirit::qi::parse(numeric2.get(), numeric2.get() + actual_len, num);
        std::wcout.precision(15);
        std::wcout << num << std::endl;
    }
}
Related