Back to Taskflow

tf::FlowBuilder class

docs/classtf_1_1FlowBuilder.html

4.0.051.3 KB
Original Source

tf::FlowBuilder class

class to build a task dependency graph

The class provides essential methods to construct a task dependency graph from which tf::Taskflow and tf::Subflow are derived.

Derived classes

class Subflowclass to construct a subflow graph from the execution of a dynamic task class Taskflowclass to create a taskflow object

Constructors, destructors, conversion operators

FlowBuilder(Graph& graph)constructs a flow builder with a graph

Public functions

template<typename C, std::enable_if_t<is_static_task_v<C>, void>* = nullptr> auto emplace(C&& callable) -> Taskcreates a static task template<typename C, std::enable_if_t<is_runtime_task_v<C>, void>* = nullptr> auto emplace(C&& callable) -> Taskcreates a runtime task template<typename C, std::enable_if_t<is_subflow_task_v<C>, void>* = nullptr> auto emplace(C&& callable) -> Taskcreates a dynamic task template<typename C, std::enable_if_t<is_condition_task_v<C>, void>* = nullptr> auto emplace(C&& callable) -> Taskcreates a condition task template<typename C, std::enable_if_t<is_multi_condition_task_v<C>, void>* = nullptr> auto emplace(C&& callable) -> Taskcreates a multi-condition task template<typename... C, std::enable_if_t<(sizeof...(C)>1), void>* = nullptr> auto emplace(C && ... callables) -> autocreates multiple tasks from a list of callable objectsvoid erase(Task task)removes a task from a taskflow template<typename T> auto composed_of(T& object) -> Taskcreates a module task for the target objectauto placeholder() -> Taskcreates a placeholder taskvoid linearize(std::vector<Task>& tasks)adds adjacent dependency links to a linear list of tasksvoid linearize(std::initializer_list<Task> tasks)adds adjacent dependency links to a linear list of tasks template<typename B, typename E, typename C, typename P = DefaultPartitioner> auto for_each(B first, E last, C callable, P part = P()) -> Taskconstructs an STL-styled parallel-for task template<typename B, typename E, typename S, typename C, typename P = DefaultPartitioner> auto for_each_index(B first, E last, S step, C callable, P part = P()) -> Taskconstructs an index-based parallel-for task template<typename R, typename C, typename P = DefaultPartitioner> auto for_each_by_index(R range, C callable, P part = P()) -> Taskconstructs an index range-based parallel-for task template<typename B, typename E, typename O, typename C, typename P = DefaultPartitioner, std::enable_if_t<is_partitioner_v<std::decay_t<P>>, void>* = nullptr> auto transform(B first1, E last1, O d_first, C c, P part = P()) -> Taskconstructs a parallel-transform task template<typename B1, typename E1, typename B2, typename O, typename C, typename P = DefaultPartitioner, std::enable_if_t<! is_partitioner_v<std::decay_t<C>>, void>* = nullptr> auto transform(B1 first1, E1 last1, B2 first2, O d_first, C c, P part = P()) -> Taskconstructs a parallel-transform task template<typename B, typename E, typename T, typename O, typename P = DefaultPartitioner> auto reduce(B first, E last, T& init, O bop, P part = P()) -> Taskconstructs an STL-styled parallel-reduction task template<typename R, typename T, typename L, typename G, typename P = DefaultPartitioner> auto reduce_by_index(R range, T& init, L lop, G gop, P part = P()) -> Taskconstructs an index range-based parallel-reduction task template<typename B, typename E, typename T, typename BOP, typename UOP, typename P = DefaultPartitioner, std::enable_if_t<is_partitioner_v<std::decay_t<P>>, void>* = nullptr> auto transform_reduce(B first, E last, T& init, BOP bop, UOP uop, P part = P()) -> Taskconstructs an STL-styled parallel transform-reduce task template<typename B1, typename E1, typename B2, typename T, typename BOP_R, typename BOP_T, typename P = DefaultPartitioner, std::enable_if_t<! is_partitioner_v<std::decay_t<BOP_T>>, void>* = nullptr> auto transform_reduce(B1 first1, E1 last1, B2 first2, T& init, BOP_R bop_r, BOP_T bop_t, P part = P()) -> Taskconstructs an STL-styled parallel transform-reduce task template<typename B, typename E, typename D, typename BOP> auto inclusive_scan(B first, E last, D d_first, BOP bop) -> Taskcreates an STL-styled parallel inclusive-scan task template<typename B, typename E, typename D, typename BOP, typename T> auto inclusive_scan(B first, E last, D d_first, BOP bop, T init) -> Taskcreates an STL-styled parallel inclusive-scan task with an initial value template<typename B, typename E, typename D, typename T, typename BOP> auto exclusive_scan(B first, E last, D d_first, T init, BOP bop) -> Taskcreates an STL-styled parallel exclusive-scan task template<typename B, typename E, typename D, typename BOP, typename UOP> auto transform_inclusive_scan(B first, E last, D d_first, BOP bop, UOP uop) -> Taskcreates an STL-styled parallel transform-inclusive scan task template<typename B, typename E, typename D, typename BOP, typename UOP, typename T> auto transform_inclusive_scan(B first, E last, D d_first, BOP bop, UOP uop, T init) -> Taskcreates an STL-styled parallel transform-inclusive scan task template<typename B, typename E, typename D, typename T, typename BOP, typename UOP> auto transform_exclusive_scan(B first, E last, D d_first, T init, BOP bop, UOP uop) -> Taskcreates an STL-styled parallel transform-exclusive scan task template<typename B, typename E, typename T, typename UOP, typename P = DefaultPartitioner> auto find_if(B first, E last, T& result, UOP predicate, P part = P()) -> Taskconstructs a task to perform STL-styled find-if algorithm template<typename B, typename E, typename T, typename UOP, typename P = DefaultPartitioner> auto find_if_not(B first, E last, T& result, UOP predicate, P part = P()) -> Taskconstructs a task to perform STL-styled find-if-not algorithm template<typename B, typename E, typename T, typename C, typename P> auto min_element(B first, E last, T& result, C comp, P part) -> Taskconstructs a task to perform STL-styled min-element algorithm template<typename B, typename E, typename T, typename C, typename P> auto max_element(B first, E last, T& result, C comp, P part) -> Taskconstructs a task to perform STL-styled max-element algorithm template<typename B, typename E, typename C> auto sort(B first, E last, C cmp) -> Taskconstructs a dynamic task to perform STL-styled parallel sort template<typename B, typename E> auto sort(B first, E last) -> Taskconstructs a dynamic task to perform STL-styled parallel sort using the std::less<T> comparator, where T is the element type

Protected variables

Graph& _graphassociated graph object

Function documentation

template<typename C, std::enable_if_t<is_static_task_v<C>, void>* = nullptr> Task tf::FlowBuilder::emplace(C&& callable)

creates a static task

Template parameters
C
Parameters
---
callable
Returns

The following example creates a static task.

tf::Task static\_task = taskflow.emplace([](){});

template<typename C, std::enable_if_t<is_runtime_task_v<C>, void>* = nullptr> Task tf::FlowBuilder::emplace(C&& callable)

creates a runtime task

Template parameters
C
Parameters
---
callable
Returns

The following example creates a runtime task.

tf::Task static\_task = taskflow.emplace([](tf::Runtime&){});

template<typename C, std::enable_if_t<is_subflow_task_v<C>, void>* = nullptr> Task tf::FlowBuilder::emplace(C&& callable)

creates a dynamic task

Template parameters
C
Parameters
---
callable
Returns

The following example creates a dynamic task (tf::Subflow) that spawns two static tasks.

tf::Task dynamic\_task = taskflow.emplace([](tf::Subflow& sf){tf::Task static\_task1 = sf.emplace([](){});tf::Task static\_task2 = sf.emplace([](){});});

template<typename C, std::enable_if_t<is_condition_task_v<C>, void>* = nullptr> Task tf::FlowBuilder::emplace(C&& callable)

creates a condition task

Template parameters
C
Parameters
---
callable
Returns

The following example creates an if-else block using one condition task and three static tasks.

tf::Taskflow taskflow;auto [init, cond, yes, no] = taskflow.emplace([] () { },[] () { return 0; },[] () { std::cout \<\< "yes\n"; },[] () { std::cout \<\< "no\n"; });// executes yes if cond returns 0, or no if cond returns 1cond.precede(yes, no);cond.succeed(init);

template<typename C, std::enable_if_t<is_multi_condition_task_v<C>, void>* = nullptr> Task tf::FlowBuilder::emplace(C&& callable)

creates a multi-condition task

Template parameters
C
Parameters
---
callable
Returns

The following example creates a multi-condition task that selectively jumps to two successor tasks.

tf::Taskflow taskflow;auto [init, cond, branch1, branch2, branch3] = taskflow.emplace([] () { },[] () { return tf::SmallVector{0, 2}; },[] () { std::cout \<\< "branch1\n"; },[] () { std::cout \<\< "branch2\n"; },[] () { std::cout \<\< "branch3\n"; });// executes branch1 and branch3 when cond returns 0 and 2cond.precede(branch1, branch2, branch3);cond.succeed(init);

template<typename... C, std::enable_if_t<(sizeof...(C)>1), void>* = nullptr> auto tf::FlowBuilder::emplace(C && ... callables)

creates multiple tasks from a list of callable objects

Template parameters
C
Parameters
---
callables
Returns

The method returns a tuple of tasks each corresponding to the given callable target. You can use structured binding to get the return tasks one by one. The following example creates four static tasks and assign them to A, B, C, and D using structured binding.

auto [A, B, C, D] = taskflow.emplace([] () { std::cout \<\< "A"; },[] () { std::cout \<\< "B"; },[] () { std::cout \<\< "C"; },[] () { std::cout \<\< "D"; });

void tf::FlowBuilder::erase(Task task)

removes a task from a taskflow

Parameters
task

Removes a task and its input and output dependencies from the graph associated with the flow builder. If the task does not belong to the graph, nothing will happen.

tf::Task A = taskflow.emplace([](){ std::cout \<\< "A"; });tf::Task B = taskflow.emplace([](){ std::cout \<\< "B"; });tf::Task C = taskflow.emplace([](){ std::cout \<\< "C"; });tf::Task D = taskflow.emplace([](){ std::cout \<\< "D"; });A.precede(B, C, D);// erase A from the taskflow and its dependencies to B, C, and Dtaskflow.erase(A);

template<typename T> Task tf::FlowBuilder::composed_of(T& object)

creates a module task for the target object

Template parameters
T
Parameters
---
object
Returns

The example below demonstrates a taskflow composition using the composed_of method.

tf::Taskflow t1, t2;t1.emplace([](){ std::cout \<\< "t1"; });// t2 is partially composed of t1tf::Task comp = t2.composed\_of(t1);tf::Task init = t2.emplace([](){ std::cout \<\< "t2"; });init.precede(comp);

The taskflow object t2 is composed of another taskflow object t1, preceded by another static task init. When taskflow t2 is submitted to an executor, init will run first and then comp which spawns its definition in taskflow t1.

The target object being composed must define the method T::graph() that returns a reference to a graph object of type tf::Graph such that it can interact with the executor. For example:

// custom structstruct MyObj {tf::Graph graph;MyObj() {tf::FlowBuilder builder(graph);tf::Task task = builder.emplace([](){std::cout \<\< "a task\n";// static task});}Graph& graph() { return graph; }};MyObj obj;tf::Task comp = taskflow.composed\_of(obj);

Task tf::FlowBuilder::placeholder()

creates a placeholder task

| Returns | a tf::Task handle |

A placeholder task maps to a node in the taskflow graph, but it does not have any callable work assigned yet. A placeholder task is different from an empty task handle that does not point to any node in a graph.

// create a placeholder task with no callable target assignedtf::Task placeholder = taskflow.placeholder();assert(placeholder.empty() == false && placeholder.has\_work() == false);// create an empty task handletf::Task task;assert(task.empty() == true);// assign the task handle to the placeholder tasktask = placeholder;assert(task.empty() == false && task.has\_work() == false);

void tf::FlowBuilder::linearize(std::vector<Task>& tasks)

adds adjacent dependency links to a linear list of tasks

Parameters
tasks

This member function creates linear dependencies over a vector of tasks.

tf::Task A = taskflow.emplace([](){ std::cout \<\< "A"; });tf::Task B = taskflow.emplace([](){ std::cout \<\< "B"; });tf::Task C = taskflow.emplace([](){ std::cout \<\< "C"; });tf::Task D = taskflow.emplace([](){ std::cout \<\< "D"; });std::vector\<tf::Task\> tasks {A, B, C, D}taskflow.linearize(tasks);// A-\>B-\>C-\>D

void tf::FlowBuilder::linearize(std::initializer_list<Task> tasks)

adds adjacent dependency links to a linear list of tasks

Parameters
tasks

This member function creates linear dependencies over a list of tasks.

tf::Task A = taskflow.emplace([](){ std::cout \<\< "A"; });tf::Task B = taskflow.emplace([](){ std::cout \<\< "B"; });tf::Task C = taskflow.emplace([](){ std::cout \<\< "C"; });tf::Task D = taskflow.emplace([](){ std::cout \<\< "D"; });taskflow.linearize({A, B, C, D});// A-\>B-\>C-\>D

template<typename B, typename E, typename C, typename P = DefaultPartitioner> Task tf::FlowBuilder::for_each(B first, E last, C callable, P part = P())

constructs an STL-styled parallel-for task

Template parameters
B
E
C
P
Parameters
---
first
last
callable
part
Returns

The task spawns asynchronous tasks that applies the callable object to each object obtained by dereferencing every iterator in the range [first, last). This method is equivalent to the parallel execution of the following loop:

for(auto itr=first; itr!=last; itr++) {callable(\*itr);}

Iterators can be made stateful by using std::reference_wrapper The callable needs to take a single argument of the dereferenced iterator type.

template<typename B, typename E, typename S, typename C, typename P = DefaultPartitioner> Task tf::FlowBuilder::for_each_index(B first, E last, S step, C callable, P part = P())

constructs an index-based parallel-for task

Template parameters
B
E
S
C
P
Parameters
---
first
last
step
callable
part
Returns

The task spawns asynchronous tasks that applies the callable object to each index in the range [first, last) with the step size. This method is equivalent to the parallel execution of the following loop:

// case 1: step size is positivefor(auto i=first; i\<last; i+=step) {callable(i);}// case 2: step size is negativefor(auto i=first, i\>last; i+=step) {callable(i);}

Iterators can be made stateful by using std::reference_wrapper The callable needs to take a single argument of the integral index type.

template<typename R, typename C, typename P = DefaultPartitioner> Task tf::FlowBuilder::for_each_by_index(R range, C callable, P part = P())

constructs an index range-based parallel-for task

Template parameters
R
C
P
Parameters
---
range
callable
part
Returns

The task spawns asynchronous tasks that applies the callable object to in the range [first, last) with the step size.

// [0, 17) with a step size of 2 using tf::IndexRangetf::IndexRange\<int\> range(0, 17, 2);// parallelize the sequence [0, 2, 4, 6, 8, 10, 12, 14, 16]taskflow.for\_each\_by\_index(range, [](tf::IndexRange\<int\> range) {// iterate each index in the subrangefor(int i=range.begin(); i\<range.end(); i+=range.step\_size()) {printf("iterate %d\n", i);}});executor.run(taskflow).wait();

The callable needs to take a single argument of type tf::IndexRange.

template<typename B, typename E, typename O, typename C, typename P = DefaultPartitioner, std::enable_if_t<is_partitioner_v<std::decay_t<P>>, void>* = nullptr> Task tf::FlowBuilder::transform(B first1, E last1, O d_first, C c, P part = P())

constructs a parallel-transform task

Template parameters
B
E
O
C
P
Parameters
---
first1
last1
d_first
c
part
Returns

The task spawns asynchronous tasks that applies the callable object to an input range and stores the result in another output range. This method is equivalent to the parallel execution of the following loop:

while (first1 != last1) {\*d\_first++ = c(\*first1++);}

Iterators can be made stateful by using std::reference_wrapper The callable needs to take a single argument of the dereferenced iterator type.

template<typename B1, typename E1, typename B2, typename O, typename C, typename P = DefaultPartitioner, std::enable_if_t<! is_partitioner_v<std::decay_t<C>>, void>* = nullptr> Task tf::FlowBuilder::transform(B1 first1, E1 last1, B2 first2, O d_first, C c, P part = P())

constructs a parallel-transform task

Template parameters
B1
E1
B2
O
C
P
Parameters
---
first1
last1
first2
d_first
c
part
Returns

The task spawns asynchronous tasks that applies the callable object to two input ranges and stores the result in another output range. This method is equivalent to the parallel execution of the following loop:

while (first1 != last1) {\*d\_first++ = c(\*first1++, \*first2++);}

Iterators can be made stateful by using std::reference_wrapper The callable needs to take two arguments of dereferenced elements from the two input ranges.

template<typename B, typename E, typename T, typename O, typename P = DefaultPartitioner> Task tf::FlowBuilder::reduce(B first, E last, T& init, O bop, P part = P())

constructs an STL-styled parallel-reduction task

Template parameters
B
E
T
O
P
Parameters
---
first
last
init
bop
part
Returns

The task spawns asynchronous tasks to perform parallel reduction over init and the elements in the range [first, last). The reduced result is store in init. This method is equivalent to the parallel execution of the following loop:

for(auto itr=first; itr!=last; itr++) {init = bop(init, \*itr);}

Iterators can be made stateful by using std::reference_wrapper

template<typename R, typename T, typename L, typename G, typename P = DefaultPartitioner> Task tf::FlowBuilder::reduce_by_index(R range, T& init, L lop, G gop, P part = P())

constructs an index range-based parallel-reduction task

Template parameters
R
T
L
G
P
Parameters
---
range
init
lop
gop
part
Returns

The task spawns asynchronous tasks to perform parallel reduction over a range with init. The reduced result is store in init. Unlike the iterator-based reduction, index range-based reduction is particularly useful for applications that benefit from SIMD optimizations or other range-based processing strategies.

const size\_t N = 1000000;std::vector\<int\> data(N);// uninitialized data vectorint res = 1;// res will participate in the reductiontaskflow.reduce\_by\_index(tf::IndexRange\<size\_t\>(0, N, 1),// final resultres,// local reducer[&](tf::IndexRange\<size\_t\> subrange, std::optional\<int\> running\_total) -\> int {int residual = running\_total ? \*running\_total : 0.0;for(size\_t i=subrange.begin(); i\<subrange.end(); i+=subrange.step\_size()) {data[i] = 1.0;residual += data[i];}printf("partial sum = %lf\n", residual);return residual;},// global reducerstd::plus\<int\>());executor.run(taskflow).wait();assert(res = N + 1);

Range can be made stateful by using std::reference_wrapper.

template<typename B, typename E, typename T, typename BOP, typename UOP, typename P = DefaultPartitioner, std::enable_if_t<is_partitioner_v<std::decay_t<P>>, void>* = nullptr> Task tf::FlowBuilder::transform_reduce(B first, E last, T& init, BOP bop, UOP uop, P part = P())

constructs an STL-styled parallel transform-reduce task

Template parameters
B
E
T
BOP
UOP
P
Parameters
---
first
last
init
bop
uop
part
Returns

The task spawns asynchronous tasks to perform parallel reduction over init and the transformed elements in the range [first, last). The reduced result is store in init. This method is equivalent to the parallel execution of the following loop:

for(auto itr=first; itr!=last; itr++) {init = bop(init, uop(\*itr));}

Iterators can be made stateful by using std::reference_wrapper

template<typename B1, typename E1, typename B2, typename T, typename BOP_R, typename BOP_T, typename P = DefaultPartitioner, std::enable_if_t<! is_partitioner_v<std::decay_t<BOP_T>>, void>* = nullptr> Task tf::FlowBuilder::transform_reduce(B1 first1, E1 last1, B2 first2, T& init, BOP_R bop_r, BOP_T bop_t, P part = P())

constructs an STL-styled parallel transform-reduce task

Template parameters
B1
E1
B2
T
BOP_R
BOP_T
P
Parameters
---
first1
last1
first2
init
bop_r
bop_t
part
Returns

The task spawns asynchronous tasks to perform parallel reduction over init and transformed elements in the range [first, last). The reduced result is store in init. This method is equivalent to the parallel execution of the following loop:

for(auto itr1=first1, itr2=first2; itr1!=last1; itr1++, itr2++) {init = bop\_r(init, bop\_t(\*itr1, \*itr2));}

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E, typename D, typename BOP> Task tf::FlowBuilder::inclusive_scan(B first, E last, D d_first, BOP bop)

creates an STL-styled parallel inclusive-scan task

Template parameters
B
E
D
BOP
Parameters
---
first
last
d_first
bop

Performs the cumulative sum (aka prefix sum, aka scan) of the input range and writes the result to the output range. Each element of the output range contains the running total of all earlier elements using the given binary operator for summation.

This function generates an inclusive scan, meaning that the N-th element of the output range is the sum of the first N input elements, so the N-th input element is included.

std::vector\<int\> input = {1, 2, 3, 4, 5};taskflow.inclusive\_scan(input.begin(), input.end(), input.begin(), std::plus\<int\>{});executor.run(taskflow).wait();// input is {1, 3, 6, 10, 15}

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E, typename D, typename BOP, typename T> Task tf::FlowBuilder::inclusive_scan(B first, E last, D d_first, BOP bop, T init)

creates an STL-styled parallel inclusive-scan task with an initial value

Template parameters
B
E
D
BOP
T
Parameters
---
first
last
d_first
bop
init

Performs the cumulative sum (aka prefix sum, aka scan) of the input range and writes the result to the output range. Each element of the output range contains the running total of all earlier elements (and the initial value) using the given binary operator for summation.

This function generates an inclusive scan, meaning the N-th element of the output range is the sum of the first N input elements, so the N-th input element is included.

std::vector\<int\> input = {1, 2, 3, 4, 5};taskflow.inclusive\_scan(input.begin(), input.end(), input.begin(), std::plus\<int\>{}, -1);executor.run(taskflow).wait();// input is {0, 2, 5, 9, 14}

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E, typename D, typename T, typename BOP> Task tf::FlowBuilder::exclusive_scan(B first, E last, D d_first, T init, BOP bop)

creates an STL-styled parallel exclusive-scan task

Template parameters
B
E
D
T
BOP
Parameters
---
first
last
d_first
init
bop

Performs the cumulative sum (aka prefix sum, aka scan) of the input range and writes the result to the output range. Each element of the output range contains the running total of all earlier elements (and the initial value) using the given binary operator for summation.

This function generates an exclusive scan, meaning the N-th element of the output range is the sum of the first N-1 input elements, so the N-th input element is not included.

std::vector\<int\> input = {1, 2, 3, 4, 5};taskflow.exclusive\_scan(input.begin(), input.end(), input.begin(), -1, std::plus\<int\>{});executor.run(taskflow).wait();// input is {-1, 0, 2, 5, 9}

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E, typename D, typename BOP, typename UOP> Task tf::FlowBuilder::transform_inclusive_scan(B first, E last, D d_first, BOP bop, UOP uop)

creates an STL-styled parallel transform-inclusive scan task

Template parameters
B
E
D
BOP
UOP
Parameters
---
first
last
d_first
bop
uop

Write the cumulative sum (aka prefix sum, aka scan) of the input range to the output range. Each element of the output range contains the running total of all earlier elements using uop to transform the input elements and using bop for summation.

This function generates an inclusive scan, meaning the Nth element of the output range is the sum of the first N input elements, so the Nth input element is included.

std::vector\<int\> input = {1, 2, 3, 4, 5};taskflow.transform\_inclusive\_scan(input.begin(), input.end(), input.begin(), std::plus\<int\>{}, [] (int item) { return -item; });executor.run(taskflow).wait();// input is {-1, -3, -6, -10, -15}

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E, typename D, typename BOP, typename UOP, typename T> Task tf::FlowBuilder::transform_inclusive_scan(B first, E last, D d_first, BOP bop, UOP uop, T init)

creates an STL-styled parallel transform-inclusive scan task

Template parameters
B
E
D
BOP
UOP
T
Parameters
---
first
last
d_first
bop
uop
init

Write the cumulative sum (aka prefix sum, aka scan) of the input range to the output range. Each element of the output range contains the running total of all earlier elements (including an initial value) using uop to transform the input elements and using bop for summation.

This function generates an inclusive scan, meaning the Nth element of the output range is the sum of the first N input elements, so the Nth input element is included.

std::vector\<int\> input = {1, 2, 3, 4, 5};taskflow.transform\_inclusive\_scan(input.begin(), input.end(), input.begin(), std::plus\<int\>{}, [] (int item) { return -item; },-1);executor.run(taskflow).wait();// input is {-2, -4, -7, -11, -16}

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E, typename D, typename T, typename BOP, typename UOP> Task tf::FlowBuilder::transform_exclusive_scan(B first, E last, D d_first, T init, BOP bop, UOP uop)

creates an STL-styled parallel transform-exclusive scan task

Template parameters
B
E
D
T
BOP
UOP
Parameters
---
first
last
d_first
init
bop
uop

Write the cumulative sum (aka prefix sum, aka scan) of the input range to the output range. Each element of the output range contains the running total of all earlier elements (including an initial value) using uop to transform the input elements and using bop for summation.

This function generates an exclusive scan, meaning the Nth element of the output range is the sum of the first N-1 input elements, so the Nth input element is not included.

std::vector\<int\> input = {1, 2, 3, 4, 5};taskflow.transform\_exclusive\_scan(input.begin(), input.end(), input.begin(), -1, std::plus\<int\>{},[](int item) { return -item; });executor.run(taskflow).wait();// input is {-1, -2, -4, -7, -11}

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E, typename T, typename UOP, typename P = DefaultPartitioner> Task tf::FlowBuilder::find_if(B first, E last, T& result, UOP predicate, P part = P())

constructs a task to perform STL-styled find-if algorithm

Template parameters
B
E
T
UOP
P
Parameters
---
first
last
result
predicate
part

Returns an iterator to the first element in the range [first, last) that satisfies the given criteria (or last if there is no such iterator). This method is equivalent to the parallel execution of the following loop:

auto find\_if(InputIt first, InputIt last, UnaryPredicate p) {for (; first != last; ++first) {if (predicate(\*first)){return first;}}return last;}

For example, the code below find the element that satisfies the given criteria (value plus one is equal to 23) from an input range of 10 elements:

std::vector\<int\> input = {1, 6, 9, 10, 22, 5, 7, 8, 9, 11};std::vector\<int\>::iterator result;taskflow.find\_if(input.begin(), input.end(), [](int i){ return i+1 = 23; }, result);executor.run(taskflow).wait();assert(\*result == 22);

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E, typename T, typename UOP, typename P = DefaultPartitioner> Task tf::FlowBuilder::find_if_not(B first, E last, T& result, UOP predicate, P part = P())

constructs a task to perform STL-styled find-if-not algorithm

Template parameters
B
E
T
UOP
P
Parameters
---
first
last
result
predicate
part

Returns an iterator to the first element in the range [first, last) that satisfies the given criteria (or last if there is no such iterator). This method is equivalent to the parallel execution of the following loop:

auto find\_if(InputIt first, InputIt last, UnaryPredicate p) {for (; first != last; ++first) {if (!predicate(\*first)){return first;}}return last;}

For example, the code below find the element that satisfies the given criteria (value is not equal to 1) from an input range of 10 elements:

std::vector\<int\> input = {1, 1, 1, 1, 22, 1, 1, 1, 1, 1};std::vector\<int\>::iterator result;taskflow.find\_if\_not(input.begin(), input.end(), [](int i){ return i == 1; }, result);executor.run(taskflow).wait();assert(\*result == 22);

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E, typename T, typename C, typename P> Task tf::FlowBuilder::min_element(B first, E last, T& result, C comp, P part)

constructs a task to perform STL-styled min-element algorithm

Template parameters
B
E
T
C
P
Parameters
---
first
last
result
comp
part

Finds the smallest element in the [first, last) using the given comparison function object. The iterator to that smallest element is stored in result. This method is equivalent to the parallel execution of the following loop:

if (first == last) {return last;}auto smallest = first;++first;for (; first != last; ++first) {if (comp(\*first, \*smallest)) {smallest = first;}}return smallest;

For example, the code below find the smallest element from an input range of 10 elements.

std::vector\<int\> input = {1, 1, 1, 1, 1, -1, 1, 1, 1, 1};std::vector\<int\>::iterator result;taskflow.min\_element(input.begin(), input.end(), std::less\<int\>(), result);executor.run(taskflow).wait();assert(\*result == -1);

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E, typename T, typename C, typename P> Task tf::FlowBuilder::max_element(B first, E last, T& result, C comp, P part)

constructs a task to perform STL-styled max-element algorithm

Template parameters
B
E
T
C
P
Parameters
---
first
last
result
comp
part

Finds the largest element in the [first, last) using the given comparison function object. The iterator to that largest element is stored in result. This method is equivalent to the parallel execution of the following loop:

if (first == last){return last;}auto largest = first;++first;for (; first != last; ++first) {if (comp(\*largest, \*first)) {largest = first;}}return largest;

For example, the code below find the largest element from an input range of 10 elements.

std::vector\<int\> input = {1, 1, 1, 1, 1, 2, 1, 1, 1, 1};std::vector\<int\>::iterator result;taskflow.max\_element(input.begin(), input.end(), std::less\<int\>(), result);executor.run(taskflow).wait();assert(\*result == 2);

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E, typename C> Task tf::FlowBuilder::sort(B first, E last, C cmp)

constructs a dynamic task to perform STL-styled parallel sort

Template parameters
B
E
C
Parameters
---
first
last
cmp

The task spawns asynchronous tasks to sort elements in the range [first, last) in parallel.

Iterators can be made stateful by using std::reference_wrapper

template<typename B, typename E> Task tf::FlowBuilder::sort(B first, E last)

constructs a dynamic task to perform STL-styled parallel sort using the std::less<T> comparator, where T is the element type

Template parameters
B
E
Parameters
---
first
last

The task spawns asynchronous tasks to parallel sort elements in the range [first, last) using the std::less<T> comparator, where T is the dereferenced iterator type.

Iterators can be made stateful by using std::reference_wrapper