Fluent::Programmer

    Home Blog About
  • Home
  • Blog
  • About
  • c++
  • beautiful-code-series
  • linux
  • algorithms
  • assembly-language
  • c-programming
  • network-programming

Beautiful code #5 - std::for_each, std::ranges::for_each, std::optional, std::execution::par_unseq, mutable 👋

  • Fluent Programmer
  • March 7, 2023
  •   4 min read

Quick summary ↬  std::for_each, std::ranges::for_each, std::optional, and std::execution::par_unseq are features from C++20 and C++23 that helps efficiently loop through the loop, loop in parallel, add optional values in the data structure of your choice, etc.

beautiful-code-5

Beautiful code #5

 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <algorithm>
#include <execution>
#include <vector>
#include <optional>
#include <iostream>

struct Custom {};
void process(Custom&) {}

int main() {
    std::vector<int> data{1, 2, 3, 4, 5};
    std::for_each(data.begin(), data.end(), [i = 5](int& v) mutable {
        // iterate over all elements in order
        v += i--;
    });
    // data == {6, 6, 6, 6, 6}
    for (auto v : data)
        std::cout << v << " ";
    std::cout << "\n";

    std::vector<Custom> rng(10,Custom{});
    std::for_each(std::execution::par_unseq, // parallel, in any order
        rng.begin(), rng.end(), // all elements
        process // invoke process on each element
    );

    std::vector<std::optional<int>> opt{{},2,{},4,{}};
    for (const auto& val : opt) {
        if (val.has_value()) {
            std::cout << val.value() << " ";
        } else {
            std::cout << "EMPTY ";
        }
    }
    std::cout << "\n" << std::endl;
    
    std::ranges::for_each(opt,
        [](int v) {
            // iterate over projected values
            // {0, 2, 0, 4, 0}
            std::cout << "value loop\n" << std::endl;
            std::cout << v << " " << "\n";
        },
        [](std::optional<int>& v){
            // projection that will return 
            // the contained value or zero
            std::cout << "projection loop\n" << std::endl;
            return v.value_or(555555);
        });
    std::cout << "\n";
}

Beautiful code #5 Explanation

The above C++ code snippet make use of all the four functions std::for_each, std::ranges::for_each, std::optional, std::execution::par_unseq, and mutable.

std::for_each as used in line number 12 accepts three arguments the first one an iterator that represents the beginning of an iterator, the second one an iterator that represents the end of an iteration and the third is a simple lambda function.

1
2
3
4
std::for_each(data.begin(), data.end(), [i = 5](int& v) mutable {
        // iterate over all elements in order
        v += i--;
    });

Curious about the mutable keyword?? It is used to make sure the value of i if updated in the loop will be maintained that way.

All the three arguments were already explained and hope it is clear.

1
2
3
4
std::for_each(std::execution::par_unseq, // parallel, in any order
        rng.begin(), rng.end(), // all elements
        process // invoke process on each element
    );
Line number 22 to 25 is another version of the same for loop but it only works in the C++17 and versions after that since std::execution is new and introduced in C++17. The feature was proposed by Pablo Halpern and Michael Garland and was accepted as part of the standardization effort. Argument 1 is std::execution::par_unseq which is to mention that this for loop can be executed in parallel and in unsequenced manner. The second and third argument is about the starting and ending iterators and the fourth argument is a method name process that is called for every value of the vector.

Now look at the line number 37 to 49 - it has an another variant of for_each using the ranges library. The first argument is the actual data structure, the second argument is a lambda function and the third one is the projection lambda function. Always the projection lambda function runs first to see whether the std::vector that stores the std::optional<int> has value or not. value_or is a method to check whether std::optional has the value or not and assign a default value if it does not has a value.

The the actual lambda function will run and print the value one by one from the std::vector.

Conclusion

I covered std::for_each, std::ranges::for_each, mutable, std::optional, std::execution::par_unseq. Hope you enjoyed it.

About The Author

Fluentprogrammer doesn't need coffee to program. They run on pure caffeine and lines of code.

Email Newsletter

Table of Contents

  • Beautiful code #5
  • Beautiful code #5 Explanation
  • Conclusion
  • C++
  • Systems & Network
  • C programming
  • Beautiful code
  • Design patterns
  • Linux
  • Open Source
  • Algorithms
  • Data Structures
  • System design
  • Distributed systems
  • Kernel
  • Assembly language
  • Hardware
  • Ultra Low Latency
  • Inspiration

Unhealthy love with dark corners of C++

Founded by an engineer to help engineers. 2021–2023.

Fluentprogrammer.com is a brand name managed by Abyan, Inc.

  • About us
  • Privacy policy