3

Basically I'm trying to create a lambda that captures an object that is moveable only (e.g unique_ptr), and take some argument as input.

I have an expensive resource that is raised by callback which I need to move to another thread for processing. The object is moveable only (and this cannot change) and the callback signature takes it by value (can be changed to rvalue reference). The following is a minimal working example of the problem at state:

void processor(std::function<void(int)> func)
{
    auto thread = std::async(std::launch::async, func, 2);
}

using ResourceType = std::unique_ptr<int>; //Example for moveable only type

void handler(ResourceType r)
{
    processor([r](int x) // error C2280: ...unique_ptr...: attempting to reference a deleted function
    {
        int usage_of_resource = *r + x;
        std::cout << usage_of_resource << std::endl;
    });
}

I tried following this question and answers (How to capture a unique_ptr into a lambda expression?) But, I can't use C++14 and the suggested answer there doesn't even compile. This is what I tried:

std::function<void(int)> getAction(ResourceType p)
{
    return std::bind([](ResourceType& r, int x)
    { 
        int usage_of_resource = *r + x;
        std::cout << usage_of_resource << std::endl; 
    }, std::move(p), std::placeholders::_1);
}
void handler(ResourceType r)
{
    processor(getAction(std::move(r)));
}

g++: error: use of deleted function

msvc: error: attempting to reference a deleted function

I have tried to sort of create my own lambda (using a struct with operator()) but I failed to get around the same issue. Eventually as a workaround I created a new pointer to my resource, and moved it there before calling the processor and then extracting it inside the lambda I pass.

Is this a real issue with the C++ standard that is only solved in C++14? or is there a way to move the object somehow (preferably an elegant one of course)?

EDIT:

Capturing the underlying resource would potentially cause a memory leak, since the processor is a fixed size message queue that drops the first element (dequeues) when the queue is full and a new element arrives. So between the time of capture and the time of usage the lambda can be dropped and thus the resource would not be freed.

ZivS
  • 2,094
  • 2
  • 27
  • 48

1 Answers1

2

The equivalent to move capture of C++14 is

class MyFunctor
{
public:
    MyFunctor(ResourceType res) : res(std::move(res)) {}

    void operator() (int x) const {
        int usage_of_resource = *res + x;
        std::cout << usage_of_resource << std::endl;
    }
private:
    ResourceType res;
};

And use it:

void handler(ResourceType r)
{
    MyFunctor f{std::move(r)};
    f(42);
}

You cannot use it for initialize anyway std::function as it requires CopyConstructible callable.

You may wrap or transfer your resource in std::shared_ptr to store it in std::function, something like:

void handler(std::unique_ptr<int> r)
{
    std::shared_ptr<int> sr = std::move(r);
    processor([sr](int x) {
        int usage_of_resource = *sr + x;
        std::cout << usage_of_resource << std::endl;
    });
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • As I wrote _"I have tried to sort of create my own lambda (using a struct with operator()) but I failed to get around the same issue"_, how can you pass `MyFunctor` around (e.g to `processor`) so that someone else will call it? – ZivS Jul 13 '17 at 20:32
  • As I said, `std::function` requires CopyConstructible callable, so you cannot use c++14 move capture lambda, of given equivalent. You have to wrap your resource in a copy constructible callable. `std::shared_ptr` might solve that. – Jarod42 Jul 13 '17 at 20:39
  • Using `std::shared_ptr` was my original w\e but I would like to avoid heap allocations in that flow so it is not an optimal solution for me. Aren't you referring to `MyFunctor` when you say _"wrap your resource in a copy constructible callable."_? if so then I don't understand how to create one... – ZivS Jul 13 '17 at 21:01