I am wrapping around a C function from freeRTOS that creates a task and takes its arguments with void pointer in C++. The function looks a little bit like this:
void createTask(TaskFunction_t taskCode, void * args);
So to my understanding to pass 2 arguments to the task I would need to create a struct, cast its address to void*, pass it and then cast it back to the original state like so:
struct Params
{
const int a;
const double b;
};
static void task(void * args)
{
auto params = *static_cast<Params*>(args);
// do something with params.a and params.b
}
int main()
{
Params params{1, 2.2};
createTask(task, static_cast<void*>(¶ms));
}
What would be preferred way of wrapping this function so that I could pass a variable number of arguments of variable types? Should I just leave void * args as an argument or is there something that could be done with templates or maybe tuples to simplify this process a little bit.
In C++11 onwards, you can use something like
static void call_task(void *args) {
auto& f = *static_cast<std::function<void()>*>(args);
f();
}
// note: need this to stay alive!
std::function<void()> f = [&](){
// Any arguments you like here
do_whatever(1, 2, 3)
};
CreateTask(call_task, static_cast<void*>(&f));
You need to ensure the lifetime of f
is longer than that of the task (just as you would for your Params
object).
You can actually avoid std::function
altogether, as:
template<typename Func>
void call_func(void *args) {
auto& f = *static_cast<Func*>(args);
f();
}
template<typename Func>
void wrapped_create_task(Func& func) {
CreateTask(call_func<Func>, static_cast<void*>(&func));
}
// you can still use `std::function` here, but you don't have to.
auto f = [&](){
// Any arguments you like here
do_whatever(1, 2, 3)
};
// this works on any object that implements `operator ()`
wrapped_create_task(f)
Again, it's really important that f
remains alive for the duration of its execution. You can't put it on a stack that dies before the task does.