Home > OS >  Ranges-v3 transform limitations
Ranges-v3 transform limitations

Time:07-07

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>>();
}
  • Related