Passing a Non-Copyable Closure Object to Std::Function Parameter

How to store non-copyable std::function into a container?

std::ref and std::cref are meant to be used in this cases to avoid copying the object (see http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper).

Not sure that I got your question right, but this compiles for me:

#include <vector>
#include <functional>

class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable &) = delete;
NonCopyable(NonCopyable &&) = default;
};

int main() {
std::vector<std::function<void()>> callbacks;
callbacks.emplace_back([] {});

NonCopyable tmp;
auto fun = std::bind([](const NonCopyable &) {}, std::cref(tmp));
callbacks.emplace_back(fun);

return 0;
}

EDIT: As mentioned in the comments, be careful about the life cycle of the referenced variable!

Why can't I convert a lambda with a non-copyable class parameter to a std::function?

The diagnostic is super misleading. While it is obvious that mutex is not copyable, there is no copying of mutex requested in the provided snippet!

The immediate cause of issue is the fact that std::function<> templated constructor (one which would be called when std::function is created from the lambda) is SFINAE-restricted on object being Callable with types of arguments provided.

Callable is not satisfied, since the calling expression tries to pass mutex by value, and mutex is not copyable.

As a result, compiler can't find any suitable constructor to construct the std::function object and issues diagnostic observed.

It also seems the inability to construct an std::function like that is somewhat unfortunate, since you would be able to call a normal function or lambda with temporary-constructed object like

f(t{});

due to guaranteed copy elision. While it makes no sense to do so for std::mutex, it could be meaningful for other non-copyable, non-movable classes.

Passing a non-static method or std::functionvoid(...) as a void (*) argument

As stated, this is not possible since there is no way to meaningfully construct a plain function pointer from a (non static) method, a closure, or a std::function object.

Roughly speaking, each of the constructs above are logically formed by two parts: some fixed code (a pointer to a fixed, statically known function), and variable data (the object at hand, or the captures for a closure). We can not simply throw away the second part.

That being said, I would recommend to check if the library will call the dispatcher passing a user-defined pointer for its void * argument. It's somewhat common to see C style library functions like

void register_callback(void (*dispatcher)(int,int,void *), void *user_data);

This is a trick to allow to simulate closures in C, passing the two "parts" of the closure separately.

If that's the case, instead of

// not working, just for example
std::function<void(int, int)> f;
register_callback(f);

You can write

// Make sure f will live long enough.
// I'm using new to stress the point, but you can use anything
// else provided that the pointer will be valid when f is
// called.
std::function<void(int, int)> *pf = new ...;
register_callback(call_the_function, pf);

provided you have defined

// fixed function, independent from the std::function object
void call_the_function(int x, int y, void *p) {
std::function<void(int,int)> *pf =
(std::function<void(int,int)>*) p;
(*pf)(x,y);
}

If you used new to keep the pointer valid long enough, remember to delete it when the callback is no longer needed.

Passing a lambda with moved capture to function

The error happens because your lambda has non-copyable captures, making the lambda itself not copyable. std::function requires that the wrapped object be copy-constructible.

If you have control over call_func, make it a template:

template<typename T>
void call_func(T&& func)
{
func();
}

int main()
{
std::fstream fs{"test.txt", std::fstream::out};
auto lam = [fs = std::move(fs)] { const_cast<std::fstream&>(fs).close(); };
call_func(lam);
}

Following is my take on your idea in (2). Since std::function requires the wrapped object to be copy-constructible, we can make our own function wrapper that does not have this restriction:

#include <algorithm>
#include <fstream>
#include <iterator>
#include <utility>
#include <memory>
#include <sstream>
#include <vector>

template<typename T>
void call_func(T&& func) {
func();
}

// All functors have a common base, so we will be able to store them in a single container.
struct baseFunctor {
virtual void operator()()=0;
};

// The actual functor is as simple as it gets.
template<typename T>
class functor : public baseFunctor {
T f;
public:
template<typename U>
functor(U&& f)
: f(std::forward<U>(f))
{}
void operator()() override {
f();
}
};

// In C++17 you don't need this: functor's default constructor can already infer T.
template<typename T>
auto makeNewFunctor(T&& v) {
return std::unique_ptr<baseFunctor>(new functor<T>{std::forward<T>(v)});
}

int main() {
// We need to store pointers instead of values, for the virtual function mechanism to behave correctly.
std::vector<std::unique_ptr<baseFunctor>> functors;

// Generate 10 functors writing to 10 different file streams
std::generate_n(std::back_inserter(functors), 10, [](){
static int i=0;
std::ostringstream oss{"test"};
oss << ++i << ".txt";
std::fstream fs{oss.str(), std::fstream::out};
return makeNewFunctor([fs = std::move(fs)] () mutable { fs.close(); });
});

// Execute the functors
for (auto& functor : functors) {
call_func(*functor);
}
}

Note that the overhead from the virtual call is unavoidable: Since you need functors with different behavior stored in the same container, you essentially need polymorphic behavior one way or the other. So you either implement this polymorphism by hand, or use virtual. I prefer the latter.

How to create an std::function from a move-capturing lambda expression?

template<class F> function(F f);

template <class F, class A> function(allocator_arg_t, const A& a, F f);

Requires: F shall be CopyConstructible. f shall be Callable for argument types ArgTypes and return type R. The copy constructor and destructor of A shall not throw exceptions.

§20.9.11.2.1 [func.wrap.func.con]

Note that operator = is defined in terms of this constructor and swap, so the same restrictions apply:

template<class F> function& operator=(F&& f);

Effects: function(std::forward<F>(f)).swap(*this);

§20.9.11.2.1 [func.wrap.func.con]

So to answer your question: Yes, it is possible to construct a std::function from a move-capturing lambda (since this only specifies how the lambda captures), but it is not possible to construct a std::function from a move-only type (e.g. a move-capturing lambda which move-captures something that is not copy constructible).

type deduction for std::function argument types with auto adds const

The problem is that generic lambdas (auto param) are equivalent to a callable object whose operator() is templated. This means that the actual type of the lambda argument is not contained in the lambda, and only deduced when the lambda is invoked.

However in your case, by having specific std::function arguments, you force a conversion to a concrete type before the lambda is invoked, so there is no way to deduce the auto type from anything. There is no SFINAE in a non-template context.

With no specific argument type, both your call are valid overloads. Actually any std::function that can match an [](auto&) is valid. Now the only rule is probably that the most cv-qualified overload wins. You can try with a volatile float& and you will see it will still choose that. Once it choose this overload, the compilation will fail when trying to invoke.

Passing a Lambda Expression to std::function in C++

The problem

The problem you have can be reduced to this:

#include <functional>

template <typename ...Args>
void foo(std::function<bool(Args...)>) { }

int main()
{
foo([](int, int) { return true; });
}

which will fail to compile. The reason for this is that the type deduction for the template arguments for std::function fails.

You expect a std::function of some sort to be passed as an argument. Since no std::function object (of whatever exact instantiation) is passed in, the compiler tries to construct a std::function and deduce the template parameter types by calling the constructor of std::function. Here starts the problem. The matching constructor in this case would be:

template <typename F>
function(F f);

You will notice that the constructor itself is also templated. The compiler could successfully deduce F as a lambda, but since F is a template parameter of the constructor, the template parameter of the class itself std::function cannot be deduced.

Furthermore, to quote cppreference on that constructor:

[...] This constructor does not participate in overload resolution
unless f is Callable for argument types Args... and return type R. [...]

This means that the existance of this constructor is based on whether F can be called with the class template arguments Args..., but since those are not explicitly defined and cannot be deduced, this constructor won't be available anyway.

The solution

Since you only use that std::function inside exportSelectedData, simply make it a template parameter alltogether (ditching the std::function part):

template<typename Func, typename ...Args_T>
std::vector<CSingleElement> exportSelectedData(uint32_t u32MutexTimeout, Func compareFn, Args_T const&...) const;

You should also change Args_T&& to Args_T const& since you don't simply forward those arguments but reuse them inside a loop.

Edit

Regarding your follow-up question in the edit: think about what you're doing.

First you declare a lambda:

auto lambda = [u32MaxCmdDuration](const CombinedDictElement& singleElement) { /* ... */ };

Now think about the signature of that lambda. You return a boolean so the return type is bool (so far so good). You take u32MaxCmdDuration in the capture-clause and you take one argument singleElement. Lets remove all the extra qualifiers and look at the signature:

bool(CombinedDictElement) // take a CombinedDictElement and return a bool

Next, we take a look at the call of exportSelectedData:

exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);

You pass in u32MutexTimeout and lambda which is perfectly fine, the lambda is captued by compareFn. The third argument is u32MaxCmdDuration which is captured by ...compareArgs in your template. Now lets take a look at where you actually invoke the lambda inside exportSelectedData:

if (compareFn(singleElement, compareArgs...)) // ...

What signature do you expect compareFn to have here? If we expand the ...compareArgs pack (again, removing the extra qualifiers for simplicity) the signature looks like this:

bool(CombinedDictElement, unsigned int) // take a CombinedDictElement and an unsigned int and return a bool

Here is the lambda signature again:

bool(CombinedDictElement)

Do you spot the problem? The lambda captures u32MaxCmdDuration as a state capture while exportSelectedData expects it as an parameter (since you passed it into exportSelectedData as an additional argument). The signatures differ from each other, obviously, so we have to change one of them to match the other. This is fairly easy in your case, either

  • change your lambda to take u32MaxCmdDuration as a parameter:

    auto lambda = [](const CombinedDictElement& singleElement, unsigned int u32MaxCmdDuration)

    or

  • remove u32MaxCmdDuration as an additional argument to your exportSelectedData call:

     exportSelectedData(u32MutexTimeout, lambda);


Related Topics



Leave a reply



Submit