1.. _std_invoke:
2
3Invoke a Callable Object
4==========================
5
6Starting from C++17, the requirements for callable objects passed to algorithms or Flow Graph nodes are relaxed. It allows using additional types of bodies.
7Previously, the body of the algorithm or Flow Graph node needed to be a Function Object (see `C++ Standard Function Object <https://en.cppreference.com/w/cpp/utility/functional>`_) and provide an
8``operator()`` that accepts input parameters.
9
10Now the body needs to meet the more relaxed requirements of being Callable (see `C++ Standard Callable <https://en.cppreference.com/w/cpp/named_req/Callable>`_) that covers three types of objects:
11
12* **Function Objects that provide operator(arg1, arg2, ...)**, which accepts the input parameters
13* **Pointers to member functions** that you can use as the body of the algorithm or the Flow Graph node
14* **Pointers to member objects** work as the body of the algorithm or parallel construct
15
16You can use it not only for a Flow Graph but also for algorithms. See the example below:
17
18.. code::
19
20    // The class models oneTBB Range
21    class StrideRange {
22    public:
23        StrideRange(int* s, std::size_t sz, std::size_t str)
24            : start(s), size(sz), stride(str) {}
25
26        // A copy constructor
27        StrideRange(const StrideRange&) = default;
28
29        // A splitting constructor
30        StrideRange(StrideRange& other, oneapi::tbb::split)
31            : start(other.start), size(other.size / 2)
32        {
33            other.size -= size;
34            other.start += size;
35        }
36
37        ~StrideRange() = default;
38
39        // Indicate if the range is empty
40        bool empty() const {
41            return size == 0;
42        }
43
44        // Indicate if the range can be divided
45        bool is_divisible() const {
46            return size >= stride;
47        }
48
49        void iterate() const {
50            for (std::size_t i = 0; i < size; i += stride) {
51                // Performed an action for each element of the range,
52                // implement the code based on your requirements
53            }
54        }
55
56    private:
57        int* start;
58        std::size_t size;
59        std::size_t stride;
60    };
61
62Where:
63
64* The ``StrideRange`` class models oneTBB range that should be iterated with a specified stride during its initial construction.
65* The ``stride`` value is stored in a private field within the range. Therefore, the class provides the member function ``iterate() const`` that implements a loop with the specified stride.
66
67``range.iterate()``
68*******************
69
70Before C++17, to utilize a range in a parallel algorithm, such as ``parallel_for``, it was required to provide a ``Function Object`` as the algorithm's body. This Function Object defined the operations to be executed on each iteration of the range:
71
72.. code::
73
74    int main() {
75        std::size_t array_size = 1000;
76
77        int* array_to_iterate = new int[array_size];
78
79        StrideRange range(array_to_iterate, array_size, /* stride = */ 2);
80
81        // Define a lambda function as the body of the parallel_for loop
82        auto pfor_body = [] (const StrideRange& range) {
83            range.iterate();
84        };
85
86        // Perform parallel iteration
87        oneapi::tbb::parallel_for(range, pfor_body);
88
89        delete[] array_to_iterate;
90    }
91
92An additional lambda function ``pfor_body`` was also required. This lambda function invoked the ``rage.iterate()`` function.
93
94Now with C++17, you can directly utilize a pointer to ``range.iterate()`` as the body of the algorithm:
95
96.. code::
97
98    int main() {
99        std::size_t array_size = 1000;
100
101        int* array_to_iterate = new int[array_size];
102
103        // Performs the iteration over the array elements with the specified stride
104        StrideRange range(array_to_iterate, array_size, /* stride = */ 2);
105
106        // Parallelize the iteration over the range object
107        oneapi::tbb::parallel_for(range, &StrideRange::iterate);
108
109        delete[] array_to_iterate;
110    }
111
112``std::invoke``
113****************
114
115``std::invoke`` is a function template that provides a syntax for invoking different types of callable objects with a set of arguments.
116
117oneTBB implementation uses the C++ standard function ``std::invoke(&StrideRange::iterate, range)`` to execute the body. It is the equivalent of ``range.iterate()``.
118Therefore, it allows you to invoke a callable object, such as a function object, with the provided arguments.
119
120.. tip:: Refer to `C++ Standard <https://en.cppreference.com/w/cpp/utility/functional/invoke>`_ to learn more about ``std::invoke``.
121
122Example
123^^^^^^^^
124
125Consider a specific scenario with ``function_node`` within a Flow Graph.
126
127In the example below, a ``function_node`` takes an object as an input to read a member object of that input and proceed it to the next node in the graph:
128
129.. code::
130
131    struct Object {
132        int number;
133    };
134
135    int main() {
136        using namespace oneapi::tbb::flow;
137
138        // Lambda function to read the member object of the input Object
139        auto number_reader = [] (const Object& obj) {
140            return obj.number;
141        };
142
143        // Lambda function to process the received integer
144        auto number_processor = [] (int i) { /* processing integer */ };
145
146        graph g;
147
148        // Function node that takes an Object as input and produces an integer
149        function_node<Object, int> func1(g, unlimited, number_reader);
150
151        // Function node that takes an integer as input and processes it
152        function_node<int, int> func2(g, unlimited, number_processor);
153
154        // Connect the function nodes
155        make_edge(func1, func2);
156
157        // Provide produced input to the graph
158        func1.try_put(Object{1});
159
160        // Wait for the graph to complete
161        g.wait_for_all();
162    }
163
164
165Before C++17, the ``function_node`` in the Flow Graph required the body to be a Function Object. A lambda function was required to extract the number from the Object.
166
167With C++17, you can use ``std::invoke`` with a pointer to the member number directly as the body.
168
169You can update the previous example as follows:
170
171.. code::
172
173    struct Object {
174        int number;
175    };
176
177    int main() {
178        using namespace oneapi::tbb::flow;
179
180        // The processing logic for the received integer
181        auto number_processor = [] (int i) { /* processing integer */ };
182
183        // Create a graph object g to hold the flow graph
184        graph g;
185
186        // Use a member function pointer to the number member of the Object struct as the body
187        function_node<Object, int> func1(g, unlimited, &Object::number);
188
189        // Use the number_processor lambda function as the body
190        function_node<int, int> func2(g, unlimited, number_processor);
191
192        // Connect the function nodes
193        make_edge(func1, func2);
194
195        // Connect the function nodes
196        func1.try_put(Object{1});
197
198       // Wait for the graph to complete
199       g.wait_for_all();
200    }
201
202Find More
203*********
204
205The following APIs supports Callable object as Bodies:
206
207* `parallel_for <https://oneapi-src.github.io/oneAPI-spec/spec/elements/oneTBB/source/algorithms/functions/parallel_for_func.html>`_
208* `parallel_reduce <https://oneapi-src.github.io/oneAPI-spec/spec/elements/oneTBB/source/algorithms/functions/parallel_reduce_func.html>`_
209* `parallel_deterministic_reduce <https://oneapi-src.github.io/oneAPI-spec/spec/elements/oneTBB/source/algorithms/functions/parallel_deterministic_reduce_func.html>`_
210* `parallel_for_each <https://oneapi-src.github.io/oneAPI-spec/spec/elements/oneTBB/source/algorithms/functions/parallel_for_each_func.html>`_
211* `parallel_scan <https://oneapi-src.github.io/oneAPI-spec/spec/elements/oneTBB/source/algorithms/functions/parallel_scan_func.html>`_
212* `parallel_pipeline <https://oneapi-src.github.io/oneAPI-spec/spec/elements/oneTBB/source/algorithms/functions/parallel_pipeline_func.html>`_
213* `function_node <https://oneapi-src.github.io/oneAPI-spec/spec/elements/oneTBB/source/flow_graph/func_node_cls.html>`_
214* `multifunction_node <https://oneapi-src.github.io/oneAPI-spec/spec/elements/oneTBB/source/flow_graph/multifunc_node_cls.html>`_
215* `async_node <https://oneapi-src.github.io/oneAPI-spec/spec/elements/oneTBB/source/flow_graph/async_node_cls.html>`_
216* `sequencer_node <https://oneapi-src.github.io/oneAPI-spec/spec/elements/oneTBB/source/flow_graph/sequencer_node_cls.html>`_
217* `join_node with key_matching policy <https://oneapi-src.github.io/oneAPI-spec/spec/elements/oneTBB/source/flow_graph/join_node_cls.html>`_
218