Posts C++ Range-v3 - A Quick Overview
Post
Cancel

C++ Range-v3 - A Quick Overview

Hi all! In this post I explore the range-v3 library, not to be confused with the native ranges library.

Functional

Create Pipes!

Create your own custom pipeable lambdas in an easy way, just create them as an argument of rg::make_pipeable, and boom. Done. That’s too cool!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <range/v3/all.hpp>
#include <fmt/ranges.h>

namespace rg = ranges;

int main()
{
    // Raise to the second power
    auto pow = rg::make_pipeable([](auto&& e){ return e*e; });
    // Take the remainder with 5
    auto mod = rg::make_pipeable([](auto&& e){ return e%5; });

    // Perform the operations
    auto num{8 | pow | mod};

    // Print result
    fmt::print("{}",num);

    return 0;
}

output: 4

Compose functions

This is pretty cool, rg::compose creates a function that is a composition of both passed as a parameter. In this example, one lambda picks the second element of the vector v, and, the second one raises to the second power. To merge this behaviours rg::compose, takes the behaviours in reverse order of application, as shown in the example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <vector>
#include <range/v3/all.hpp>
#include <fmt/ranges.h>

namespace rg = ranges;

int main()
{
    std::vector<int> v{1,2,3,4,5};
    
    auto second = [](auto&& v){ return v.at(1); };
    auto pow = [](auto&& e){ return e*e; };
    auto second_pow = rg::compose(pow,second);

    fmt::print("{}",second_pow(v));

    return 0;
}

output: 4

Parameter type checks

Pretty useful for concept validation, when used with static_assetions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <concepts>
#include <cstdint>
#include <type_traits>
#include <range/v3/all.hpp>
#include <fmt/ranges.h>

namespace rg = ranges;

template<typename T>
concept Integral = std::is_integral_v<T>;

int main()
{
    auto sum = []<Integral I>(I&& e){ return e+e; };

    fmt::print("{}\n",rg::is_invocable_v<decltype(sum),int8_t>);
    fmt::print("{}\n",rg::is_invocable_v<decltype(sum),int16_t>);
    fmt::print("{}\n",rg::is_invocable_v<decltype(sum),int32_t>);
    fmt::print("{}\n",rg::is_invocable_v<decltype(sum),int64_t>);
    fmt::print("{}\n",rg::is_invocable_v<decltype(sum),float>);
    fmt::print("{}\n",rg::is_invocable_v<decltype(sum),double>);
    fmt::print("{}\n",rg::is_invocable_v<decltype(sum),long double>);
    return 0;
}

output: true true true true false false false

Algorithms

Sum the elements of a container

This task is quite easy with ranges, with the rg::accumulate method, the first parameter is the range to reverse, rv::all(v) encapsulates vector v into a range; the second parameter is added to the final sum, thus, rg::accumulate(rv::all(v),10) yields the value 16.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <vector>
#include <iostream>
#include <range/v3/all.hpp>
#include <fmt/core.h>

namespace rg = ranges;
namespace rv = ranges::views;

int main()
{
    std::vector<int> v{1,2,3};
    auto sum = rg::accumulate(rv::all(v),0);
    fmt::print("{}\n", sum);
    return 0;
}

Output: 6

Reverse a container

This is pretty straightforward, I have a thing for the |= operator, looks cool.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <vector>
#include <iostream>
#include <concepts>
#include <range/v3/all.hpp>
#include <fmt/ranges.h>

namespace ra = ranges::actions;

int main()
{
    std::vector<int> v{1,2,3};
    v |= ra::reverse;
    fmt::print("{}\n", v);
    return 0;
}

Output: {3, 2, 1}

Split a container in n chunks

Splits a container into n sub containers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <vector>
#include <iostream>
#include <range/v3/all.hpp>
#include <fmt/ranges.h>

namespace rg = ranges;
namespace rv = ranges::views;

int main()
{
    std::vector<int> v{1,2,3,4,5};
    auto chunks {rv::chunk(rv::all(v),2) | rg::to<std::vector<std::vector<int>>>};
    rg::for_each(chunks,[](auto&& c){ fmt::print("{}\n", c); });
    return 0;
}

output: {1, 2} {3, 4} {5}

Intersperse an element

Inserts an element in-between every element in the container.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <vector>
#include <iostream>
#include <range/v3/all.hpp>
#include <fmt/ranges.h>

namespace rg = ranges;
namespace rv = ranges::views;

int main()
{
    std::vector<int> v{1,2,3,4,5};
    auto interspersed {rv::intersperse(rv::all(v),30) | rg::to<std::vector>};
    fmt::print("{}\n", interspersed);
    return 0;
}

output: {1, 30, 2, 30, 3, 30, 4, 30, 5}

Find an element

Searches for an element in a very concise way.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <vector>
#include <range/v3/all.hpp>
#include <fmt/ranges.h>

namespace rg = ranges;

int main()
{
    std::vector<int> v{1,2,3,4,5};
    auto search {rg::find(v,3)};
    (search != rg::end(v))? fmt::print("{}\n", *search) : fmt::print("Not item in vector");
    return 0;
}

output: 3

Get a reference to an element

Similar to std::reference_wrapper, but… shorter!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <vector>
#include <range/v3/all.hpp>
#include <fmt/ranges.h>

namespace rg = ranges;

int main()
{
    std::vector<int> v{1,2,3,4,5};
    auto ref {rg::ref(v.at(2))};
    ref.get() = 8;
    fmt::print("{}",v);
    return 0;
}

output: {1, 2, 8, 4, 5}

This post is licensed under CC BY 4.0 by the author.

Contents

-

-

Trending Tags