Functor usage example? Intended behavior?
wilderfield opened this issue · comments
I was trying to create a basic example that uses a stateful functor.
The documentation says functors or class objects with operator() overloaded can be used as a task.
However, when I tried this out, it seems that the class object is copied.
The state is lost.
Are lambda captures the only way to share state across tasks?
What I really want to do is create a data flow pipeline.
TaskA reads input buffer A, fills output buffer A.
TaskB reads output buffer A, fills output buffer B.
TaskC reads output buffer B, fills output buffer C.
^ Sequential for now, but some parallel to come.
#include <taskflow/taskflow.hpp> // the only include you need
int main(){
tf::Executor executor;
tf::Taskflow taskflow("simple");
// A Functor
class increment
{
public:
int num;
increment(int n) : num(n) { }
void operator () (void) {
num += 1;
std::cout << num << std::endl;
std::cout << &num << std::endl;
}
};
increment inc(1);
auto [A, B] = taskflow.emplace(
inc,
inc
);
A.precede(B); // A runs before B
executor.run(taskflow).wait();
std::cout << inc.num << std::endl;
taskflow.dump(std::cout);
return 0;
}
This prints:
2
0x55963e39a440
2
0x55963e39a348
1
digraph Taskflow {
subgraph cluster_p0x7ffdd552b700 {
label="Taskflow: simple";
p0x55963e39a290[label="p0x55963e39a290" ];
p0x55963e39a388[label="p0x55963e39a388" ];
p0x55963e39a388 -> p0x55963e39a290;
}
}
I got some help from ChatGPT, and this is working how I wanted it to.
I can take a class method and bind it to a std::function object.
Then it works as I want it to.
However, if you have suggestions on cleanest way to build dataflow graph, it would be appreciated.
#include <taskflow/taskflow.hpp> // the only include you need
int main(){
tf::Executor executor;
tf::Taskflow taskflow("simple");
class increment
{
public:
int num;
increment(int n) : num(n) { }
void add(void) {
num += 1;
std::cout << num << std::endl;
std::cout << &num << std::endl;
}
};
increment inc(1);
std::function<void(void)> func = std::bind(&increment::add, &inc);
auto [A, B] = taskflow.emplace(
func,
func
);
A.precede(B); // A runs before B
executor.run(taskflow).wait();
std::cout << inc.num << std::endl;
taskflow.dump(std::cout);
return 0;
}
@wilderfield yes, under the hood, Taskflow use std::function
to store each task. Per std::function
definition, it will store the target with copy semantics. So you will need to either use std::bind
or create another lambda to explicitly call the inc
with reference semantics
auto [A, B] = taskflow.emplace(
[&](){ inc(); },
[&](){ inc(); }
)
Thank you!