Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rebinding lambda in c++11... is it possible?

Tags:

c++

c++11

lambda

I have a situation where I have a lambda as a member variable that is created by a certain function call. The problem is that it captures this as a part of its operation. Later on, I want to be able to copy the whole object...

However, at the time of the copy I don't know how the lambda was created (it could have been defined in several spots through different code paths). Hence, I'm somewhat at a loss as to what to put in the copy constructor. Ideally, I would want to "rebind" the lambda's captures to the new "this" that was created.

Is this at all possible?

Here's some sample code:

#include <iostream>
#include <string>
#include <functional>

class Foo
{
  public:

    Foo () = default;
    ~Foo () = default;

    void set (const std::string & v)
    {
        value = v;
    }

    void set ()
    {
        lambda = [&]()
        {
            return this->value;
        };
    }

    std::string get ()
    {
        return lambda();
    }


    std::string value;
    std::function <std::string (void)> lambda;
};

int main ()
{
    Foo foo;

    foo.set ();
    foo.set ("first");

    std::cerr << foo.get () << std::endl; // prints "first"

    foo.set ("captures change");

    std::cerr << foo.get () << std::endl; // prints "captures change"

    Foo foo2 (foo);
    foo2.set ("second");

    std::cerr << foo.get () << std::endl; // prints "captures change" (as desired)
    std::cerr << foo2.get () << std::endl; // prints "captures change" (I would want "second" here)

    return 0;
}

Thanks in advance.

like image 961
vmpstr Avatar asked Feb 20 '23 16:02

vmpstr


2 Answers

The problem you're seeing is that the this pointer is being captured into the lambda, but you're now executing the copy of the function from another object. It's working in your example because both objects exist, but it's a dangling pointer waiting to happen.

The cleanest way to do this is to modify your std::function and your lambdas to take an argument of a pointer to the class, and use that passed in pointer instead of capturing this. Depending on the contents of your lambda, you can choose to capture the values.

class Foo
{
  public:

    Foo () = default;
    ~Foo () = default;

    void set (const std::string & v)
    {
        value = v;
    }

    void set ()
    {
        lambda = [](Foo* self)
        {
            return self->value;
        };
    }

    std::string get ()
    {
        return lambda(this);
    }


    std::string value;
    std::function <std::string (Foo*)> lambda;
};

Example at IDEOne

like image 84
Dave S Avatar answered Feb 28 '23 02:02

Dave S


I don't think you can modify the closure. If you need the function to operate on another object, you need to pass the pointer to the object as an argument to the function:

class Foo
{
  public:

    Foo () = default;
    ~Foo () = default;

    void set (const std::string & v)
    {
        value = v;
    }

    void set ()
    {
        lambda = [](Foo* t)
        {
            return t->value;
        };
    }

    std::string get ()
    {
        return lambda(this);
    }

    std::string value;
    std::function <std::string (Foo*)> lambda;
};
like image 33
Henrik Avatar answered Feb 28 '23 01:02

Henrik