Using C and range-v3 library, what's the optimal approach to converting a string with space-separated numbers to a vector of integers?
I tried the following code:
#include <iostream>
#include <range/v3/all.hpp>
using namespace std::literals;
int main() {
auto r = "1 1 2 3 5 8 13"sv
| ranges::views::split(" "sv)
| ranges::views::transform([](auto &&i){ return std::stoi(std::string{i}); })
| ranges::to<std::vector<int>>();
for (auto i: r)
std::cout << "Value: " << i << std::endl;
}
It doesn't compile however. In clang, the error is as follows:
repro-range.cpp:10:60: error: no matching constructor for initialization of 'std::string' (aka 'basic_string<char>')
| ranges::view::transform([](auto &&i){ return std::stoi(std::string{i}); })
^ ~~~
It seems that the type of i
is ranges::detail::split_outer_iterator
and it's not convertible to string. Actually, I don't understand how to use i
, can't dereference it, can't convert it to anything useful... replacing string_views by strings also doesn't improve the situation.
What's weird, the code below works fine:
auto r = "1 1 2 3 5 8 13"sv
| ranges::views::split(" "sv)
| ranges::to<std::vector<std::string>>();
which suggest me the problem is netiher split
nor to
, but the transform
itself.
How to make the first piece code working?
CodePudding user response:
Digging deeper, I found out that i
in my example isn't an iterator nor a wrapper over string_view (like I expected) but a range of characters (a special type with begin
and end
iterators).
Meaning, my code works if I first convert i
to a string the range way:
auto r = "1 1 2 3 5 8 13"sv
| ranges::views::split(" "sv)
| ranges::views::transform([](auto &&i){
return std::stoi(i | ranges::to<std::string>());
})
| ranges::to<std::vector<int>>();
Although I'll be delighted if somebody posts a nicer (at least less verbose) way to do that.
CodePudding user response:
If you have a string containing space separated numbers you can first create an std::istringstream
over the string and then use ranges::istream
to parse the numbers (assuming int
s here):
auto s = "1 1 2 3 5 8 13";
auto ss = std::istringstream{s};
auto r = ranges::istream<int>(ss)
| ranges::to<std::vector<int>>();
Here's a demo.