Search code examples
c++algorithmboostfindrange-v3

How to emulate boost::algorithm::find_if_backward with Range-v3?


I've been using extensively boost::algorithm::find_if_backward to get a forward iterator to the last element in a range satisfying a predicate.

How do I accomplish the same task using Range-v3?

This is my attempt, which looks a bit clunky; and I'm not even sure is robust enough. Actually, as suggested in a comment, the code is not robust enough, because when no element is found, range_it_to_last_2 ends up being std::next(v.begin(), -1), which is undefined behavior, I believe.

#include <algorithm>
#include <boost/algorithm/find_backward.hpp>
#include <boost/hana/functional/partial.hpp>
#include <iostream>
#include <range/v3/algorithm/find_if.hpp>
#include <range/v3/view/reverse.hpp>

using boost::algorithm::find_if_backward;
using ranges::find_if;
using ranges::views::reverse;

auto constexpr is_2 = boost::hana::partial(std::equal_to<>{}, 2);

int main() {
    std::vector<int> v{0,1,2,2,2,3};

    // What I have been doing so far:
    auto boost_it_to_last_2 = find_if_backward(v, is_2);

    // The Range-v3 analogous I could come up with, but it's ugly:
    auto range_it_to_last_2 = std::next(find_if(v | reverse, is_2).base(), -1);

    for (auto it = v.begin(); it <= boost_it_to_last_2; ++it) {
        std::cout << *it << ' ';
    } // prints 0 1 2 2 2
    std::cout << std::endl;
    for (auto it = v.begin(); it <= range_it_to_last_2; ++it) {
        std::cout << *it << ' ';
    } // prints 0 1 2 2 2
    std::cout << std::endl;
}

Solution

  • Assuming you always know that a match is found, why not simplify to the following, getting the identical output:

    Live On Godbolt

    #include <algorithm>
    #include <boost/algorithm/find_backward.hpp>
    #include <boost/hana/functional/partial.hpp>
    #include <fmt/ranges.h>
    #include <range/v3/algorithm/find_if.hpp>
    #include <range/v3/view/subrange.hpp>
    #include <range/v3/view/reverse.hpp>
    
    using boost::algorithm::find_if_backward;
    using ranges::find_if;
    using ranges::views::reverse;
    using ranges::subrange;
    
    auto constexpr pred = boost::hana::partial(std::equal_to<>{}, 2);
    
    int main() {
        std::vector<int> v {0,1,2,2,2,3};
        auto boost_match = find_if_backward(v, pred);
        auto range_match = find_if(v | reverse, pred).base();
    
        static_assert(std::is_same_v<decltype(boost_match), decltype(range_match)>);
    
        fmt::print("boost: {}\nrange: {}\n",
                subrange(v.begin(), boost_match+1),
                subrange(v.begin(), range_match));
    }
    

    Prints

    boost: {0, 1, 2, 2, 2}
    range: {0, 1, 2, 2, 2}
    

    (Some toy respellings for fun: https://godbolt.org/z/ccPKeo)