Back to Taskflow

Taskflow: A General

docs/classtf_1_1Task.html

4.1.040.0 KB
Original Source

| | Taskflow: A General-purpose Task-parallel Programming System |

Loading...

Searching...

No Matches

Public Member Functions | Friends | List of all members

tf::Task Class Reference

class to create a task handle over a taskflow node More...

#include <taskflow/core/task.hpp>

|

Public Member Functions

| | | Task ()=default | | | constructs an empty task
| | | | | Task (const Task &other) | | | constructs the task with the copy of the other task
| | | | Task & | operator= (const Task &other) | | | replaces the contents with a copy of the other task
| | | | Task & | operator= (std::nullptr_t) | | | replaces the contents with a null pointer
| | | | bool | operator== (const Task &rhs) const | | | compares if two tasks are associated with the same taskflow node
| | | | bool | operator!= (const Task &rhs) const | | | compares if two tasks are not associated with the same taskflow node
| | | | const std::string & | name () const | | | queries the name of the task
| | | | size_t | num_successors () const | | | queries the number of successors of the task
| | | | size_t | num_predecessors () const | | | queries the number of predecessors of the task
| | | | size_t | num_strong_dependencies () const | | | queries the number of strong dependencies of the task
| | | | size_t | num_weak_dependencies () const | | | queries the number of weak dependencies of the task
| | | | Task & | name (const std::string &name) | | | assigns a name to the task
| | | | template<typename C> | | Task & | work (C &&callable) | | | assigns a callable
| | | | template<GraphLike T> | | Task & | composed_of (T &object) | | | creates a module task from a taskflow
| | | | Task & | adopt (tf::Graph &&graph) | | | creates a module task from a graph by taking over its ownership
| | | | template<typename... Ts> | | Task & | precede (Ts &&... tasks) | | | adds precedence links from this to other tasks
| | | | template<typename... Ts> | | Task & | succeed (Ts &&... tasks) | | | adds precedence links from other tasks to this
| | | | template<typename... Ts> | | Task & | remove_predecessors (Ts &&... tasks) | | | removes predecessor links from other tasks to this
| | | | template<typename... Ts> | | Task & | remove_successors (Ts &&... tasks) | | | removes successor links from this to other tasks
| | | | Task & | release (Semaphore &semaphore) | | | makes the task release the given semaphore
| | | | template<typename I> | | Task & | release (I first, I last) | | | makes the task release the given range of semaphores
| | | | Task & | acquire (Semaphore &semaphore) | | | makes the task acquire the given semaphore
| | | | template<typename I> | | Task & | acquire (I first, I last) | | | makes the task acquire the given range of semaphores
| | | | Task & | data (void *data) | | | assigns pointer to user data
| | | | void | reset () | | | resets the task handle to null
| | | | void | reset_work () | | | resets the associated work to a placeholder
| | | | bool | empty () const | | | queries if the task handle is associated with a taskflow node
| | | | bool | has_work () const | | | queries if the task has a work assigned
| | | | template<typename V> | | void | for_each_successor (V &&visitor) const | | | applies an visitor callable to each successor of the task
| | | | template<typename V> | | void | for_each_predecessor (V &&visitor) const | | | applies an visitor callable to each predecessor of the task
| | | | template<typename V> | | void | for_each_subflow_task (V &&visitor) const | | | applies an visitor callable to each subflow task
| | | | size_t | hash_value () const | | | obtains a hash value of the underlying node
| | | | TaskType | type () const | | | returns the task type
| | | | void | dump (std::ostream &ostream) const | | | dumps the task through an output stream
| | | | void * | data () const | | | queries pointer to user data
| | | | std::exception_ptr | exception_ptr () const | | | retrieves the exception pointer of this task
| | | | bool | has_exception_ptr () const | | | queries if the task has an exception pointer
| | |

|

Friends

| | class | FlowBuilder | | | | class | Runtime | | | | class | NonpreemptiveRuntime | | | | class | Taskflow | | | | class | TaskView | | | | class | Executor | | |

Detailed Description

class to create a task handle over a taskflow node

A task points to a node in a taskflow graph and provides a set of methods for users to access and modify attributes of the associated node, such as dependencies, callable, names, and so on. A task is a very lightweight object (i.e., it only stores a node pointer) and can be trivially copied around.

// create two tasks with one dependency

auto task1 = taskflow.emplace({}).name("task1");

auto task2 = taskflow.emplace({}).name("task2");

task1.precede(task2);

// dump the task information through std::cout

task1.dump(std::cout);

tf::Task::name

const std::string & name() const

queries the name of the task

Definition task.hpp:1388

A task created from a taskflow can be one of the following types:

tf::Task task1 = taskflow.emplace({}).name("static task");

tf::Task task2 = taskflow.emplace({ return 3; }).name("condition task");

tf::Task task3 = taskflow.emplace({}).name("runtime task");

tf::Task task4 = taskflow.emplace([](tf::Subflow& sf){

tf::Task stask1 = sf.emplace({});

tf::Task stask2 = sf.emplace({});

}).name("subflow task");

tf::Task task5 = taskflow.composed_of(taskflow2).name("module task");

tf::FlowBuilder::emplace

Task emplace(C &&callable)

creates a static task

Definition flow_builder.hpp:1571

tf::Runtime

class to create a runtime task

Definition runtime.hpp:47

tf::Task

class to create a task handle over a taskflow node

Definition task.hpp:569

tf::Task::composed_of

Task & composed_of(T &object)

creates a module task from a taskflow

Definition task.hpp:1290

A tf::Task is polymorphic. Once created, you can assign a different task type to it using tf::Task::work. For example, the code below creates a static task and then reworks it to a subflow task:

tf::Task task = taskflow.emplace({}).name("static task");

task.work([](tf::Subflow& sf){

tf::Task stask1 = sf.emplace({});

tf::Task stask2 = sf.emplace({});

}).name("subflow task");

tf::Subflow

class to construct a subflow graph from the execution of a dynamic task

Definition flow_builder.hpp:1735

tf::Task::work

Task & work(C &&callable)

assigns a callable

Definition task.hpp:1491

Attentiontf::Task does not own the lifetime of the associated node. Accessing the attributes of the associated node after the taskflow has been destroyed can result in undefined behavior.

Constructor & Destructor Documentation

Task() [1/2]

|

| tf::Task::Task | ( | | ) | |

| default |

constructs an empty task

An empty task is not associated with any node in a taskflow.

Task() [2/2]

|

| tf::Task::Task | ( | const Task & | other | ) | |

| inline |

constructs the task with the copy of the other task

Parameters

| other | the other task to copy |

tf::Taskflow taskflow;

tf::Task A = taskflow.emplace({ std::cout << "Task A\n"; });

tf::Task B(A);

assert(B == A); // Now, B and A refer to the same underlying node

tf::Taskflow

class to create a taskflow object

Definition taskflow.hpp:64

Member Function Documentation

acquire() [1/2]

template<typename I>

| Task & tf::Task::acquire | ( | I | first, | | | | I | last ) |

makes the task acquire the given range of semaphores

NoteTo know more about tf::Semaphore, please refer to Limit the Maximum Concurrency.

acquire() [2/2]

|

| Task & tf::Task::acquire | ( | Semaphore & | semaphore | ) | |

| inline |

makes the task acquire the given semaphore

NoteTo know more about tf::Semaphore, please refer to Limit the Maximum Concurrency.

adopt()

|

| Task & tf::Task::adopt | ( | tf::Graph && | graph | ) | |

| inline |

creates a module task from a graph by taking over its ownership

Parameters

| graph | the graph to adopt (moved into the task) |

Returnsa Task handle to the adopted module task

Unlike tf::Task::composed_of, which references an externally-owned tf::Taskflow, adopt transfers ownership of the given tf::Graph into the task. The graph's lifetime is managed by the executor once adopted, and the caller has no access to the moved-from graph afterward.

tf::Taskflow taskflow;

tf::Graph g;

tf::FlowBuilder{g}.emplace([]{ std::cout << "task in adopted graph\n"; });

taskflow.adopt(std::move(g)).name("adopted");

tf::FlowBuilder

class to build a task dependency graph

Definition flow_builder.hpp:22

tf::FlowBuilder::adopt

Task adopt(Graph &&graph)

creates a module task from a graph by taking over its ownership

Definition flow_builder.hpp:1628

tf::Graph

class to create a graph object

Definition graph.hpp:47

NotePlease refer to Composable Tasking for details.

composed_of()

template<GraphLike T>

| Task & tf::Task::composed_of | ( | T & | object | ) | |

creates a module task from a taskflow

Template Parameters

| T | type satisfying tf::GraphLike |

Parameters

| object | a custom object that defines T::graph() method |

Returns*this

The example below creates a module task from a taskflow:

task.composed_of(taskflow);

To understand how Taskflow schedules a module task including how to create a schedulable graph, pleas refer to Create a Custom Composable Graph.

data() [1/2]

|

| void * tf::Task::data | ( | | ) | const |

| inline |

queries pointer to user data

ReturnsC-styled pointer to the attached user data by tf::Task::data(void* data)

The following example shows how to attach a user data to a task and retrieve it during the execution of the task.

tf::Executor executor;

tf::Taskflow taskflow("attach data to a task");

int data; // user data

// create a task and attach it a user data

auto A = taskflow.placeholder();

A.data(&data).work(A{

auto d = *static_cast<int*>(A.data());

std::cout << "data is " << d << std::endl;

});

// run the taskflow iteratively with changing data

for(data = 0; data<10; data++){

executor.run(taskflow).wait();

}

tf::Executor

class to create an executor

Definition executor.hpp:62

tf::Executor::run

tf::Future< void > run(Taskflow &taskflow)

runs a taskflow once

tf::Task::data

void * data() const

queries pointer to user data

Definition task.hpp:1515

tf::Task::data

Task & data(void *data)

assigns pointer to user data

Definition task.hpp:1520

data() [2/2]

|

| Task & tf::Task::data | ( | void * | data | ) | |

| inline |

assigns pointer to user data

Parameters

| data | pointer to user data |

Returns*this

The following example shows how to attach a user data to a task and retrieve it during the execution of the task.

tf::Executor executor;

tf::Taskflow taskflow("attach data to a task");

int data; // user data

// create a task and attach it a user data

auto A = taskflow.placeholder();

A.data(&data).work(A{

auto d = *static_cast<int*>(A.data());

std::cout << "data is " << d << std::endl;

});

// run the taskflow iteratively with changing data

for(data = 0; data<10; data++){

executor.run(taskflow).wait();

}

dump()

|

| void tf::Task::dump | ( | std::ostream & | ostream | ) | const |

| inline |

dumps the task through an output stream

The method dumps the name and the type of this task through the given output stream.

task.dump(std::cout);

empty()

|

| bool tf::Task::empty | ( | | ) | const |

| inline |

queries if the task handle is associated with a taskflow node

Returnstrue if the task is not associated with any taskflow node; otherwise false

tf::Task task;

assert(task.empty() == true);

tf::Task::empty

bool empty() const

queries if the task handle is associated with a taskflow node

Definition task.hpp:1413

Note that an empty task is not equal to a placeholder task. A placeholder task is created from tf::Taskflow::placeholder and is associated with a taskflow node, but its work is not assigned yet.

exception_ptr()

|

| std::exception_ptr tf::Task::exception_ptr | ( | | ) | const |

| inline |

retrieves the exception pointer of this task

This method retrieves the exception pointer of this task that are silently caught by the executor, if any. When multiple tasks throw exceptions concurrently, only one exception will be propagated, while the others are silently caught and stored within their respective tasks. For example, in the code below, both tasks B and C throw exceptions. However, only one of them will be propagated to the try-catch block, while the other will be silently caught and stored within its respective task.

tf::Executor executor(2);

tf::Taskflow taskflow;

std::atomic<size_t> arrivals(0);

auto [B, C] = taskflow.emplace(

& {

// wait for two threads to arrive so we avoid premature cancellation

++arrivals; while(arrivals != 2);

throw std::runtime_error("oops");

},

& {

// wait for two threads to arrive so we avoid premature cancellation

++arrivals; while(arrivals != 2);

throw std::runtime_error("oops");

}

);

try {

executor.run(taskflow).get();

}

catch (const std::runtime_error& e) {

std::cerr << e.what();

}

// exactly one holds an exception as another was propagated to the try-catch block

assert((B.exception_ptr() != nullptr) != (C.exception_ptr() != nullptr));

for_each_predecessor()

template<typename V>

| void tf::Task::for_each_predecessor | ( | V && | visitor | ) | const |

applies an visitor callable to each predecessor of the task

Template Parameters

| V | a callable type (function, lambda, etc.) that accepts a tf::Task handle |

Parameters

| visitor | visitor to apply to each predecessor task |

This method allows you to traverse and inspect predecessor tasks of this task. For instance, the code below iterates the two predecessors (task2 and task3) of task1.

auto [task1, task2, task3] = taskflow.emplace(

{ std::cout << "task 1\n"; },

{ std::cout << "task 2\n"; },

{ std::cout << "task 3\n"; }

});

task1.succeed(task2, task3);

task1.for_each_predecessor([](tf::Task predecessor){

std::cout << "predecessor task " << predecessor.name() << '\n';

});

for_each_subflow_task()

template<typename V>

| void tf::Task::for_each_subflow_task | ( | V && | visitor | ) | const |

applies an visitor callable to each subflow task

Template Parameters

| V | a callable type (function, lambda, etc.) that accepts a tf::Task handle |

Parameters

| visitor | visitor to apply to each subflow task |

This method allows you to traverse and inspect tasks within a subflow. It only applies to a subflow task.

tf::Task task = taskflow.emplace([](tf::Subflow& sf){

tf::Task stask1 = sf.emplace({}).name("stask1");

tf::Task stask2 = sf.emplace({}).name("stask2");

});

// Iterate tasks in the subflow and print each subflow task.

task.for_each_subflow_task([](tf::Task stask){

std::cout << "subflow task " << stask.name() << '\n';

});

tf::Task::for_each_subflow_task

void for_each_subflow_task(V &&visitor) const

applies an visitor callable to each subflow task

Definition task.hpp:1468

for_each_successor()

template<typename V>

| void tf::Task::for_each_successor | ( | V && | visitor | ) | const |

applies an visitor callable to each successor of the task

Template Parameters

| V | a callable type (function, lambda, etc.) that accepts a tf::Task handle |

Parameters

| visitor | visitor to apply to each subflow task |

This method allows you to traverse and inspect successor tasks of this task. For instance, the code below iterates the two successors (task2 and task3) of task1.

auto [task1, task2, task3] = taskflow.emplace(

{ std::cout << "task 1\n"; },

{ std::cout << "task 2\n"; },

{ std::cout << "task 3\n"; }

});

task1.precede(task2, task3);

task1.for_each_successor([](tf::Task successor){

std::cout << "successor task " << successor.name() << '\n';

});

has_exception_ptr()

|

| bool tf::Task::has_exception_ptr | ( | | ) | const |

| inline |

queries if the task has an exception pointer

The method checks whether the task holds a pointer to a silently caught exception.

has_work()

|

| bool tf::Task::has_work | ( | | ) | const |

| inline |

queries if the task has a work assigned

Returnstrue if the task has a work assigned (not placeholder); otherwise false

tf::Task task = taskflow.placeholder();

assert(task.has_work() == false);

// assign a static task callable to this task

task.work({});

assert(task.has_work() == true);

tf::Task::has_work

bool has_work() const

queries if the task has a work assigned

Definition task.hpp:1418

hash_value()

|

| size_t tf::Task::hash_value | ( | | ) | const |

| inline |

obtains a hash value of the underlying node

Returnsthe hash value of the underlying node

The method returns std::hash on the underlying node pointer.

tf::Task task = taskflow.emplace({});

std::cout << "hash value of task is " << task.hash_value() << '\n';

tf::Task::hash_value

size_t hash_value() const

obtains a hash value of the underlying node

Definition task.hpp:1477

name() [1/2]

|

| const std::string & tf::Task::name | ( | | ) | const |

| inline |

queries the name of the task

Returnsthe name of the task as a constant string reference

tf::Task task = taskflow.emplace({});

task.name("MyTask");

std::cout << "Task name: " << task.name() << std::endl;

name() [2/2]

|

| Task & tf::Task::name | ( | const std::string & | name | ) | |

| inline |

assigns a name to the task

Parameters

| name | a std::string |

Returns*this

tf::Task task = taskflow.emplace({}).name("foo");

assert(task.name*) == "foo");

num_predecessors()

|

| size_t tf::Task::num_predecessors | ( | | ) | const |

| inline |

queries the number of predecessors of the task

Returnsthe number of predecessor tasks

tf::Task A = taskflow.emplace({});

tf::Task B = taskflow.emplace({});

A.precede(B); // A is a predecessor of B

std::cout << "B has " << B.num_predecessors() << " predecessor(s)." << std::endl;

tf::Task::precede

Task & precede(Ts &&... tasks)

adds precedence links from this to other tasks

Definition task.hpp:1258

tf::Task::num_predecessors

size_t num_predecessors() const

queries the number of predecessors of the task

Definition task.hpp:1393

num_strong_dependencies()

|

| size_t tf::Task::num_strong_dependencies | ( | | ) | const |

| inline |

queries the number of strong dependencies of the task

Returnsthe number of strong dependencies to this task

A strong dependency is a preceding link from one non-condition task to another task. For instance, task cond below has one strong dependency, while tasks yes and no each have one weak dependency.

auto [init, cond, yes, no] = taskflow.emplace(

[] () { },

[] () { return 0; },

[] () { std::cout << "yes\n"; },

[] () { std::cout << "no\n"; }

);

cond.succeed(init)

.precede(yes, no); // executes yes if cond returns 0

// executes no if cond returns 1

Embedded content

NoteTo understand how Taskflow schedule tasks under strong and weak dependencies, please refer to Conditional Tasking.

num_successors()

|

| size_t tf::Task::num_successors | ( | | ) | const |

| inline |

queries the number of successors of the task

Returnsthe number of successor tasks.

tf::Task A = taskflow.emplace({});

tf::Task B = taskflow.emplace({});

A.precede(B); // B is a successor of A

std::cout << "A has " << A.num_successors() << " successor(s)." << std::endl;

tf::Task::num_successors

size_t num_successors() const

queries the number of successors of the task

Definition task.hpp:1408

num_weak_dependencies()

|

| size_t tf::Task::num_weak_dependencies | ( | | ) | const |

| inline |

queries the number of weak dependencies of the task

Returnsthe number of weak dependencies to this task

A weak dependency is a preceding link from one condition task to another task. For instance, task cond below has one strong dependency, while tasks yes and no each have one weak dependency.

auto [init, cond, yes, no] = taskflow.emplace(

[] () { },

[] () { return 0; },

[] () { std::cout << "yes\n"; },

[] () { std::cout << "no\n"; }

);

cond.succeed(init)

.precede(yes, no); // executes yes if cond returns 0

// executes no if cond returns 1

Embedded content

NoteTo understand how Taskflow schedule tasks under strong and weak dependencies, please refer to Conditional Tasking.

operator!=()

|

| bool tf::Task::operator!= | ( | const Task & | rhs | ) | const |

| inline |

compares if two tasks are not associated with the same taskflow node

Parameters

| rhs | the other task to compare with |

Returnstrue if they refer to different nodes; false otherwise

tf::Task A = taskflow.emplace({ std::cout << "A\n"; });

tf::Task B = taskflow.emplace({ std::cout << "B\n"; });

assert(A != B); // A and B refer to different nodes

operator=() [1/2]

|

| Task & tf::Task::operator= | ( | const Task & | other | ) | |

| inline |

replaces the contents with a copy of the other task

Parameters

| other | the other task to copy |

tf::Task A = taskflow.emplace({ std::cout << "A\n"; });

tf::Task B;

B = A; // B now refers to the same node as A

operator=() [2/2]

|

| Task & tf::Task::operator= | ( | std::nullptr_t | ptr | ) | |

| inline |

replaces the contents with a null pointer

tf::Task A = taskflow.emplace({ std::cout << "A\n"; });

A = nullptr; // A no longer refers to any node

operator==()

|

| bool tf::Task::operator== | ( | const Task & | rhs | ) | const |

| inline |

compares if two tasks are associated with the same taskflow node

Parameters

| rhs | the other task to compare with |

Returnstrue if both tasks refer to the same node; false otherwise

tf::Task A = taskflow.emplace({ std::cout << "A\n"; });

tf::Task B = A;

assert(A == B); // A and B refer to the same node

precede()

template<typename... Ts>

| Task & tf::Task::precede | ( | Ts &&... | tasks | ) | |

adds precedence links from this to other tasks

Template Parameters

| Ts | parameter pack |

Parameters

| tasks | one or multiple tasks |

Returns*this

The example below creates a taskflow of two tasks, where task1 runs before task2.

auto [task1, task2] = taskflow.emplace(

{ std::cout << "task1\n"; },

{ std::cout << "task2\n"; }

);

task1.precede(task2);

release() [1/2]

template<typename I>

| Task & tf::Task::release | ( | I | first, | | | | I | last ) |

makes the task release the given range of semaphores

NoteTo know more about tf::Semaphore, please refer to Limit the Maximum Concurrency.

release() [2/2]

|

| Task & tf::Task::release | ( | Semaphore & | semaphore | ) | |

| inline |

makes the task release the given semaphore

NoteTo know more about tf::Semaphore, please refer to Limit the Maximum Concurrency.

remove_predecessors()

template<typename... Ts>

| Task & tf::Task::remove_predecessors | ( | Ts &&... | tasks | ) | |

removes predecessor links from other tasks to this

Template Parameters

| Ts | parameter pack |

Parameters

| tasks | one or multiple tasks |

Returns*this

This method removes the dependency links where the given tasks are predecessors of this task (i.e., tasks -> this). It ensures both sides of the dependency are updated to maintain graph consistency.

tf::Task A = taskflow.emplace({});

tf::Task B = taskflow.emplace({});

tf::Task C = taskflow.emplace({});

// create a linear chain of tasks, A->B->C

B.succeed(A)

.precede(C);

assert(B.num_successors() == 1 && C.num_predecessors() == 1);

// remove C from B's successor list

C.remove_predecessors(B);

assert(B.num_successors() == 0 && C.num_predecessors() == 0);

tf::Task::succeed

Task & succeed(Ts &&... tasks)

adds precedence links from other tasks to this

Definition task.hpp:1266

tf::Task::remove_predecessors

Task & remove_predecessors(Ts &&... tasks)

removes predecessor links from other tasks to this

Definition task.hpp:1274

remove_successors()

template<typename... Ts>

| Task & tf::Task::remove_successors | ( | Ts &&... | tasks | ) | |

removes successor links from this to other tasks

Template Parameters

| Ts | parameter pack |

Parameters

| tasks | one or multiple tasks |

Returns*this

This method removes the dependency links where this task is a predecessor of the given tasks (i.e., this -> tasks). It ensures both sides of the dependency are updated to maintain graph consistency.

tf::Task A = taskflow.emplace({});

tf::Task B = taskflow.emplace({});

tf::Task C = taskflow.emplace({});

// create a linear chain of tasks, A->B->C

B.succeed(A)

.precede(C);

assert(B.num_successors() == 1 && C.num_predecessors() == 1);

// remove C from B's successor list

B.remove_successors(C);

assert(B.num_successors() == 0 && C.num_predecessors() == 0);

tf::Task::remove_successors

Task & remove_successors(Ts &&... tasks)

removes successor links from this to other tasks

Definition task.hpp:1282

reset()

|

| void tf::Task::reset | ( | | ) | |

| inline |

resets the task handle to null

Resetting a task will remove its associated taskflow node and make it an empty task.

tf::Task task = taskflow.emplace({});

assert(task.empty() == false);

task.reset();

assert(task.empty() == true);

tf::Task::reset

void reset()

resets the task handle to null

Definition task.hpp:1378

succeed()

template<typename... Ts>

| Task & tf::Task::succeed | ( | Ts &&... | tasks | ) | |

adds precedence links from other tasks to this

Template Parameters

| Ts | parameter pack |

Parameters

| tasks | one or multiple tasks |

Returns*this

The example below creates a taskflow of two tasks, where task1 runs before task2.

auto [task1, task2] = taskflow.emplace(

{ std::cout << "task1\n"; },

{ std::cout << "task2\n"; }

);

task2.succeed(task1);

type()

|

| TaskType tf::Task::type | ( | | ) | const |

| inline |

returns the task type

A task can be one of the types defined in tf::TaskType and can be printed in a human-readable form using tf::to_string.

auto task = taskflow.emplace({}).name("task");

std::cout << task.name() << " type=[" << tf::to_string(task.type()) << "]\n";

tf::to_string

const char * to_string(TaskType type)

convert a task type to a human-readable string

Definition task.hpp:66

work()

template<typename C>

| Task & tf::Task::work | ( | C && | callable | ) | |

assigns a callable

Template Parameters

| C | callable type |

Parameters

| callable | callable to construct a task |

Returns*this

A tf::Task is polymorphic. Once created, you can reassign it to a different callable of a different task type using tf::Task::work. For example, the code below creates a static task and reworks it to a subflow task:

tf::Task task = taskflow.emplace({}).name("static task");

task.work([](tf::Subflow& sf){

tf::Task stask1 = sf.emplace({});

tf::Task stask2 = sf.emplace({});

}).name("subflow task");


The documentation for this class was generated from the following file: