I am trying to use ranges-v3 to split an SNMP OID into parts and return them as a std::deque<uint32_t>
.
The following code works but only after I added a number of additional un-natural steps:
#include <range/v3/all.hpp>
/// split the supplied string into nodes, using '.' as a delimiter
/// @param the path to split , e.g "888.1.2.3.4"
/// @return a std::deque<uint32_t> containing the split paths
static std::deque<uint32_t> splitPath(std::string_view path) {
constexpr std::string_view delim{"."};
auto tmp = path | ranges::views::split(delim)
| ranges::to<std::vector<std::string>>()
;
return tmp | ranges::views::transform([](std::string_view v) {
return std::stoul(std::string{v}); })
| ranges::to<std::deque<uint32_t>>();
}
Initially I expected the following to simply work:
static std::deque<uint32_t> splitPath(std::string_view path) {
constexpr std::string_view delim{"."};
return path | ranges::views::split(delim)
| ranges::views::transform([](std::string_view v) {
return std::stoul(std::string{v}); })
| ranges::to<std::deque<uint32_t>>();
}
But that results in the following error:
error: no match for ‘operator|’ (operand types are
‘ranges::split_view<std::basic_string_view<char>,
std::basic_string_view<char> >’ and
‘ranges::views::view_closure<ranges::detail::
bind_back_fn_<ranges::views::transform_base_fn, ahk::snmp::
{anonymous}::splitPath(std::string_view)::<lambda(std::string_view)> > >’)
36 | return path | ranges::views::split(delim)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| ranges::split_view<std::basic_string_view<char>,
std::basic_string_view<char> >
37 | | ranges::views::transform([](std::string_view v) {
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| ranges::views::view_closure<ranges::detail::bind_back_fn_
<ranges::views::transform_base_fn, ahk::snmp::
{anonymous}::splitPath(std::string_view)::<lambda(std::string_view)> > >
38 | return std::stoul(std::string{v}); })
Why is it necessary to convert the result of the first operation to a std::vector
and store in a named value (tmp
) before calling ranges::views::transform
? Even the following code (which removes the named value tmp
fails:
static std::deque<uint32_t> splitPath(std::string_view path) {
constexpr std::string_view delim{"."};
return path | ranges::views::split(delim)
| ranges::to<std::vector<std::string>>()
| ranges::views::transform([](std::string_view v) {
return std::stoul(std::string{v}); })
| ranges::to<std::deque<uint32_t>>();
}
CodePudding user response:
The value type of the range returned by ranges::views::split
isn't std::string_view
, it is a implementation detail type.
I'm not sure why you were able to | to<std::vector<std::string>>
at all.
Because it uses a sentinel, you will need to convert it to a common range (prior to C 20, when std::string_view
is constructible from an iterator and sentinel, or C 23 when it is constructible from a range).
std::deque<uint32_t> splitPath(std::string_view path) {
constexpr std::string_view delim{"."};
auto toul = [](auto v){
auto c = v | ranges::views::common;
return std::stoul(std::string(c.begin(), c.end()));
};
return path | ranges::views::split(delim)
| ranges::views::transform(toul)
| ranges::to<std::deque<uint32_t>>();
}