2

I have to call a function via a low level system routine, which gets the function and argument passed.

void doLater(void* func, void* arg);

I want to call the callLater function with any function and arguments using bind. So:

template<typename Call>
void caller(void *arg) {
    std::unique_ptr<Call> upCall(static_cast<Call*>(arg));
    (*upCall)();
}

template<class Function, class... Args>
void callLater(Function &&f, Args&&...args) {
  typedef decltype(std::bind(std::forward<Function>(f), std::forward<Args>(args)...))) Call;
  Call* call = new Call(std::bind(std::forward<Function>(f), std::forward<Args>(args)...));
  doLater(caller<Call>, (void*) call);
}

Using this with std::unique_ptr leads into unwanted copies, so it cannot be compiled.

std::function<void(std::unique_ptr<int>)> foo = [](std::unique_ptr<int> i) {
  std::cout << "Hello world!\n";
};
std::unique_ptr<int> value = std::make_unique<int>(1);

callLater(foo, std::move(value));

use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr...

One possible workaround is to use std::__bind_simple like it is used in std::thread (gcc) but it is not standardised. Any idea how to fix this problem?

Live example.

Viatorus
  • 1,804
  • 1
  • 18
  • 41

1 Answers1

0

Here is a proof of concept that you could probably massage into the form you're looking for.

arg parameter to callLater:

Firstly callLater takes the argument as a void*, so you're going to need to pass a pointer to it.

As such, I'd suggest allocating a std::function on the heap, and then passing the address of it as arg

Since you can only pass 1 argument, and that argument has to be the pointer to the std::function, you have to bind all the parameters which you want to pass to the function the std::function will call.

As such, the std::function needs to be of the form std::function<void()>.

std::function<void()>* fn = new std::function<void()>;
// have to bind all arguments here
*fn = std::bind(func, i, d);

func parameter to callLater:

For the function void* passed to callLater, that will have to be a free function that casts the void* arg to a std::function pointer, and then dereferences that pointer to call its operator()()

void arg_to_func(void* arg)
{
    std::unique_ptr<std::function<void()>> fn(reinterpret_cast<std::function<void()>*>(arg));
    (*fn)();
}

Working example:

Here's a working example binding to a function and to a lambda:

#include <iostream>
#include <functional>
#include <memory>

void callLater(void* f, void* arg)
{
    using fp = void(*)(void*);
    fp func = (fp)f;

    (*func)(arg);
}

using Call = std::function<void()>;

void arg_to_func(void* arg)
{
    std::unique_ptr<Call> fn(reinterpret_cast<Call*>(arg));
    (*fn)();
}

void func(int i, double d)
{
    std::cout << "func " << i << ' ' << d << '\n';
}

int main()
{
    // arguments we're going to capture
    int i = 5;
    double d = 2.3;

    Call* fn;

    // using std::bind to capture the variables
    fn = new Call;
    *fn = std::bind(func, i, d);

    callLater((void*)arg_to_func, fn);

    // using a lambda to capture the variables
    fn = new Call;
    *fn = [=](){ std::cout << "lambda " << i << ' ' << d << '\n'; };

    callLater((void*)arg_to_func, fn);

    return 0;
}

Output:

func 5 2.3
lambda 5 2.3
Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • Thank you for your answer. I know, that the function must be . Your are missing the important point to use std::bind with unique_ptr as parameters. – Viatorus Nov 10 '16 at 07:19
  • @Viatorus in that case, your question already has an answer - please see [here](http://stackoverflow.com/questions/20268482/binding-functions-with-unique-ptr-arguments-to-stdfunctionvoid). `std::unique_ptr` is not `CopyConstructible`, which is [a requirement of `std::function`](http://en.cppreference.com/w/cpp/utility/functional/function). There are techniques which may be of help though. See [here](http://stackoverflow.com/questions/8640393/move-capture-in-lambda/20669290#20669290) – Steve Lorimer Nov 10 '16 at 14:13