ranges split

This code works as expected and produces the expected output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <string_view>
#include <ranges>
#include <iostream>
#include <vector>
#include <iomanip>
#include <cctype>

template<typename T = std::string_view>
auto split(const std::string_view str, const std::string_view delims = ",") {
	std::vector<T> output;

	for (const auto& s : str | std::views::split(delims)) {
		auto word {s | std::views::drop_while(isspace) | std::views::reverse | std::views::drop_while(isspace) | std::views::reverse};

		output.emplace_back(std::to_address(word.begin()), std::to_address(word.end()));
	}

	return output;
}

int main() {
	const auto vs { split<std::string_view>(" qw, er, ty") };

	for (const auto& v : vs)
		std::cout << std::quoted(v) << '\n';
}



"qw"
"er"
"ty"


However I don't like std::to_address() in L15 - and our discussions haven't come up with a working alternative. Is there a 'ranges' way of doing this?
Last edited on
A view can be adapted to be viewed as a common range.

common_view can be useful for working with legacy algorithms that expect the iterator and sentinel are of the same type.
https://en.cppreference.com/w/cpp/ranges/common_view#Notes
OK, but this doesn't compile. What am I doing wrong?

1
2
3
4
5
6
7
8
9
10
11
12
13
template<typename T = std::string_view>
auto split(const std::string_view str, const std::string_view delims = ",") {
	std::vector<T> output;

	for (const auto& s : str | std::views::split(delims)) {
		auto word {s | std::views::drop_while(isspace) | std::views::reverse | std::views::drop_while(isspace) | std::views::reverse};
		auto cv { std::ranges::common_view {word }};

		output.emplace_back(std::to_address(word.begin()), std::to_address(word.end()));
	}

	return output;
}


L7 gives the error "C2641: cannot deduce template arguments for 'std::ranges::common_view'" with VS
Compiling this as C++20 or C++latest? I remember VS 2022 (and 2019) had problems with a handful C++20 features such as ranges as C++20 until recent updates.
Last edited on
VS2022 17.3.4 with C++ latest .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<typename T = std::string_view>
auto split(const std::string_view str, const std::string_view delims = ",") {
	std::vector<T> output;

	for (const auto& s : str | std::views::split(delims)) {
		auto word {s | std::views::drop_while(isspace) | std::views::reverse | std::views::drop_while(isspace) | std::views::reverse};
		auto cv { word | std::views::common };

		//output.emplace_back(cv.begin(), cv.end());
		output.emplace_back(std::to_address(word.begin()), std::to_address(word.end()));
	}

	return output;
}


L7 compiles - but why using std::views when the std::ranges version doesn't???

However L9 doesn't compile - with a load of standard library errors. The first being "xmemory(678,18): error C2672: 'std::construct_at': no matching overloaded function found"

Ahhh....


VS2022 17.3.4

17.3.5 was released a couple of hours ago, though I doubt the upgrade will fix your issues.

Though it could.
The reason you needed those ghastly to_address calls is that reverse/drop_while/reverse chain of range view adaptors lost contiguousness; it produced a random_access range. A plain split would produce contiguous range and you'd be able to just use string_view(w.begin(), w.end()) as on https://en.cppreference.com/w/cpp/ranges/split_view#Example

I think a more range-y thing is to use ranges::to (now part of C++23, but I'll use range-v3 so it can work)
1
2
3
4
	return str
           | ranges::view::split_when(
                   [&](char c){ return delims.contains(c) || isspace(c); })
           | ranges::to<std::vector<std::string>>();
demo: https://godbolt.org/z/P3K5cEzc4
...it won't give you a vector of string views either though.

Even more rangey is to just keep working with a range until you have to iterate it, never stuffing it into a container until you must (here, quoted's argument is where something must be done since it doesn't take range views;
1
2
3
4
5
	return str
           | ranges::view::split_when(
                   [=](char c){ return delims.contains(c) || isspace(c); });
...
	std::cout << std::quoted( v | ranges::to<std::string> ) << '\n';
https://godbolt.org/z/8M3zven73

or you could implement quoted on range views.. or indeed, do that that s.begin(), s.end() construction, at this point, with to_address to account for lost contiguousness (I wonder if v3's split_when has a bug, there's no reason it should downgrade to random-access)
Last edited on
Thanks for the explanation and suggestions.
For info, we've decided to use:

1
2
3
4
5
6
7
8
9
10
11
12
13
template<typename T = std::string_view>
requires std::same_as<T, std::string> || std::same_as<T, std::string_view>
auto split(const std::string_view str, const std::string_view delims = ",") {
	std::vector<T> output;

	for (const auto& s : str | std::views::split(delims)) {
		const std::string_view sv { s.begin(), s.end() };

		output.emplace_back(std::ranges::find_if_not(sv, isspace), std::ranges::find_if_not(sv | std::views::reverse, isspace).base());
	}

	return output;
}

Topic archived. No new replies allowed.