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