I have the following structure that has a member function pointer. My question is, is there a way for the lambda that I pass to it to refer to its own member variable? i.e.
struct Foo
{
int aVar{1};
int (*funcPtr)(int);
Foo(int (*func)(int)) : funcPtr(func) {};
};
auto bar1 = Foo([](int a) {std::cout << a;}) // ok
auto bar2 = Foo([](int a) {std::cout << a + bar2.aVar;}) // Not ok but is there a way to access the member variable of the object currently being defined?
Can I achieve something to this effect?
What I would like to achieve here is a process to automatically generate objects based on the lambda you pass in. e.g: bar2 above is an object that can return anything plus its stored value. i.e. I would like bar2.funcPtr(5) == 5 + bar2.aVar to be an invariant of the class. In the future I might need another object that can return anything minus its stored value, and I only need to pass the corresponding lambda to do that (if the lambda can access the member fields), without defining a new class or method.
The lambda must have the signature int(int) but your lambda has the signature void(int) so that's the first problem.
The other is that the lambda must capture bar2. You could use std::function for that.
#include <iostream>
#include <functional>
struct Foo {
int aVar{1};
std::function<int(int)> funcPtr;
Foo(std::function<int(int)> func) : funcPtr(func) {};
int call(int x) { return funcPtr(x); }
};
int main() {
Foo bar2([&](int a) { return a + bar2.aVar; });
std::cout << bar2.call(2); // prints 3
}
A more practical solution would be to not tie the lambda to the instance for which it was originally created but to let it take a Foo& as an argument (and not to capture it). Moving it around doesn't become such a hassle with this approach.
Example:
struct Foo {
int aVar;
int(*funcPtr)(Foo&, int); // takes a Foo& as an argument
Foo(int x, int(*func)(Foo&, int)) : aVar(x), funcPtr(func) {};
int call(int x) { return funcPtr(*this, x); }
};
int main() {
Foo bar2(10, [](Foo& f, int a) { return a + f.aVar; });
std::cout << (bar2.call(5) == bar2.aVar + 5) << '\n'; // true
Foo bar3(20, [](Foo& f, int a) { return a * f.aVar; });
std::cout << bar2.call(2) << '\n'; // 10 + 2 = 12
std::cout << bar3.call(2) << '\n'; // 20 * 2 = 40
std::swap(bar2, bar3);
std::cout << bar2.call(2) << '\n'; // swapped, now 40
std::cout << bar3.call(2) << '\n'; // swapped, now 12
}
Foo( /* ... */ )
As used in the shown code this is a constructor call. This is constructing a new object, right there.
Before the new object can be created and its constructor get invoked, the parameters to the constructor must be evaluated. This is how C++ works. There are no alternatives, or workarounds, that end up constructing the object first and only then evaluate its constructor's parameters afterwards.
For this reason it is logically impossible for a lambda, that gets passed as parameter to the constructor, "refer to its own member variable". There is nothing in existence that has "its own member variable" at this point. The object's construction has not began, and you cannot refer to an object or a member of an object that does not exist. The object cannot be constructed until the constructor's parameters get evaluated, first. C++ does not work this way.
You will need to come up with some alternative mechanism for your class. At which point you will discover another fatal design flaw that dooms the shown approach:
int (*funcPtr)(int);
This is a plain function pointer. In order for lambda to reference an object that it's related to, in some form or fashion, it must capture the object (by reference, most likely). However lambdas that capture (by value or reference), cannot be converted to a plain function pointer. Only capture-less lambdas can be converted to a plain pointer.
At the bare minimum this must be a std::function, instead.
And now that it's a std::function, you can capture its object, by reference, in the lambda, and assign it to the std::function.
But this is not all, there is another problem that you must deal with: in order for all of this to work it is no longer possible for the object to be moved or copied in any way. This is because the lambda captured a reference to the original object that was constructed, full stop.
And the fact that the constructed object gets copied or moved does not, in some form or fashion, modify the lambda so it now magically captures the reference to the copy or the moved instance of the original object.
None of these are insurmountable problems, but they will require quite a bit of work to address, in order to have a well-formed C++ program as a result.
Related
Looking at various examples with lambda expressions, I came across unexpected behavior for me. In this code, I expect that the variables captured by value will not change inside the lambda. Code execution shows the opposite. Can someone give an explanation. Thanks.
#include <iostream>
#include <functional>
using namespace std;
class Fuu{
public:
Fuu(int v):m_property(v){}
void setProperty(int v ){m_property = v;}
std::function<void(int)> lmbSet {[=](int v){ m_property = v;}};
std::function<void(char *)> lmbGet {[=](char * v){ cout<< v << " "<< m_property << endl;}};
std::function<void()> lmb4Static {[=]{ cout<<"static: "<< s_fild << endl;}};
void call() const {
lmbGet("test") ;
}
static int s_fild ;
int m_property;
};
int Fuu::s_fild = 10;
int main()
{
Fuu obj(3);
obj.call();
obj.setProperty(5);
obj.call();//expect 3
obj.lmbSet(12);
obj.call(); //expect 3
obj.lmb4Static();
Fuu::s_fild = 11;
obj.lmb4Static(); //expect 10
return 0;
}
That is a common problem. Common enough that they deprecated the implicit capture of this in a lambda with = for C++20. When your code is doing is:
std::function<void(int)> lmbSet {[=](int v){ m_property = v;}};
It is effectively doing this:
std::function<void(int)> lmbSet {[this](int v){ this->m_property = v;}};
The lambda captures the this pointer by copy, not m_property at all.
In general, but formally in C++20, the preferred way to do this would be to explicitly copy this (in which case, no = is necessary, though you could):
std::function<void(int)> lmbSet {[this](int v){ m_property = v;}};
To make a copy of the current object and modify that copy, you'd need to dereference this (and then the capture is a copy of *this) and to modify it, you'd need to mark your lambda mutable:
std::function<void(int)> lmbSet {[*this](int v) mutable { m_property = v;}};
It doesn't seem very useful, but perhaps that helps explain what's going on.
[Note: it's not valid to copy *this until the construction completes, or it's copying an object before its lifetime starts, and that will result in undefined behavior.]
Capture by value only takes copies of local variables (none in your example) and this pointer. Capturing this by value is equivalent to capturing non-static data members by reference. Static member variables, like globals, are not captured at all - you are accessing them directly instead.
If you actually need to keep a copy of non-local data, you can do it using "generalized capture" syntax which was added in C++14:
[m_property=m_property](char * v){ cout<< v << " "<< m_property << endl;}
In this case [=] effectively just copies this as a pointer. Then, when you access data members in your closure the copy of this is dereferenced, and if you modify that member it modifies the original member. If you think about it, it isn't possible that [=] copies each member. You would have an infinite recursion of copies as each lmbSet would make a new copy of lmbGet, which in turn would make a new copy of lmbSet.
Note that this solution requires that the rule of 3/5/0 be applied. If you try to make a copy of an instance of Fuu, the captured this pointer will copied and point to the original instance and not to the new copy. In general, capturing this or pointers to data members in default member initializes should be avoided.
I'm reading the C++ Programming Language by B. Stroustrup in its section 11.4.3.4 "mutable Lambdas", which says the following:
Usually, we don’t want to modify the state of the function object (the
closure), so by default we can’t. That is, the operator()() for the
generated function object (§11.4.1) is a const member function. In the
unlikely event that we want to modify the state (as opposed to
modifying the state of some variable captured by reference; §11.4.3),
we can declare the lambda mutable.
I don't understand why the default for the operator()() is const when the variable is captured by value. What's the rational for this? What could go wrong when I change the value of a variable, which is copied into the function object?
One can think of lambdas as classes with operator()(), which by default is defined as const. That is, it cannot change the state of the object. Consequently, the lambda will behave as a regular function and produce the same result every time it is called. If instead, we declare the lambda as mutable, it is possible for the lambda to modify the internal state of the object, and provide a different result for different calls depending on that state. This is not very intuitive and therefore discouraged.
For example, with mutable lambda, this can happen:
#include <iostream>
int main()
{
int n = 0;
auto lam = [=]() mutable {
n += 1;
return n;
};
std::cout << lam() << "\n"; // Prints 1
std::cout << n << "\n"; // Prints 0
std::cout << lam() << "\n"; // Prints 2
std::cout << n << "\n"; // Prints 0
}
It is easier to reason about const data.
By defaulting const, brief lamndas are easier to reason about. If you want mutability you can ask for it.
Many function objects in std are copied around; const objects that are copied have simpler state to track.
I have a std::function pointing to a function. Inside this function I change the pointer to another function.
std::function<void()> fun;
void foo() {
std::cout << "foo\n";
}
void bar() {
std::cout << "bar\n";
fun = foo;
}
int main() {
fun = bar;
fun();
fun();
}
I can't see any problem and it works just fine (see here), however I'm not sure if this is legal to do so. Is there anything I am missing? Maybe in the c++ standard draft (I checked quickly but didn't see anything so far).
This is legal with function pointers.
When you assign or construct a std::function with a target, it creates a copy of the target. In the case of assigning a function to the std::function, this in effect stores a function pointer as the target object.
When you invoke operator(), it is required to return what would happen if you invoked that the target with the arguments.
Within that "body" of the copy of the function object stored as a copy in the std::function, if you reassign to the std::function, this destroys the old target function object.
Destroying a function pointer has no impact on the validity of code executed within the function pointed to.
However, if you had stored function objects (lambdas, manual ones, other std::functions, std::bind, etc), at the point of assignment you'd run into the usual rules of running a method in a class when this is destroyed. In short, you would no longer be able to do anything that relied on "local state" of your instance.
std::function<void()> fun;
struct bob {
std::string name;
bob* next = 0;
void operator()() const {
std::cout << name << "\n";
if (next) fun = *next;
// undefined behavior:
// std::cout << name << "\n";
}
};
bob foo = {"foo"};
bob bar = {"bar", &foo};
int main() {
fun = bar;
fun();
fun();
}
live example.
So as you can see, this can be fragile.
It may come back to bite you if you do it without due consideration and in code documentation, but there is no logical reason why it won't work.
In c++, the address of a function is not needed, either within the function in the return coding.
If it didn't work in some language the complier probably wouldn't accept it - if it's a half decent compiler.
In the code below, I create a lambda that captures a local variable by reference. Note that it is a pointer, so, if C++ lambdas are true closures, it should survive the lifetime of the function that creates the lambda.
However, when I call it again, rather than creating a new local variable (a new environment) it reuses the same as before, and in fact, captures exactly the same pointer as before.
This seems wrong. Either, C++ lambdas are not true closures, or is my code incorrect?
Thank you for any help
#include <iostream>
#include <functional>
#include <memory>
std::function<int()> create_counter()
{
std::shared_ptr<int> counter = std::make_shared<int>(0);
auto f = [&] () -> int { return ++(*counter); };
return f;
}
int main()
{
auto counter1 = create_counter();
auto counter2 = create_counter();
std::cout << counter1() << std::endl;
std::cout << counter1() << std::endl;
std::cout << counter2() << std::endl;
std::cout << counter2() << std::endl;
std::cout << counter1() << std::endl;
return 0;
}
This code returns:
1
2
3
4
5
But I was expecting it to return:
1
2
1
2
3
Further edit:
Thank you for pointing the error in my original code. I see now that what is happening is that the pointer gets deleted after the invocation of create_couter, and the new create simply reuses the same memory address.
Which brings me to my real question then, what I want to do is this:
std::function<int()> create_counter()
{
int counter = 0;
auto f = [&] () -> int { return ++counter; };
return f;
}
If C++ lambdas were true closures, each local counter will coexist with the returned function (the function carries its environment--at least part of it). Instead, counter is destroyed after the invocation of create_counter, and calling the returned function creates a segmentation fault. That is not the expected behaviour of a closure.
Marco A has suggested a work around: make the pointer passed by copy. That increases the reference counter, so it does not get destroyed after create_counter. But that is kludge. But, as Marco pointed out, it works and does exactly what I was expecting.
Jarod42 proposes to declare the variable, and initialize it as part of the capture list. But that defeats the purpose of the closure, as the variables are then local to the function, not to the environment where the function is created.
apple apple proposes using a static counter. But that is a workaround to avoid the destruction of the variable at the end of create_function, and it means that all returned functions share the same variable, not the environment under which they run.
So i guess the conclusion (unless somebody can shed more light) is that lambdas in C++ are not true closures.
thank you again for your comments.
The shared pointer is being destroyed at the end of the function scope and the memory is being freed: you're storing a dangling reference
std::function<int()> create_counter()
{
std::shared_ptr<int> counter = std::make_shared<int>(0);
auto f = [&]() -> int { return ++(*counter); };
return f;
} // counter gets destroyed
Therefore invoking undefined behavior. Test it for yourself by substituting the integer with a class or struct and check if the destructor actually gets called.
Capturing by value would have incremented the usage counter of the shared pointer and prevented the problem
auto f = [=]() -> int { return ++(*counter); };
^
As mentioned, you have dangling reference as the local variable is destroyed at end of the scope.
You can simplify your function to
std::function<int()> create_counter()
{
int counter = 0;
return [=] () mutable -> int { return ++counter; };
}
or even (in C++14)
auto create_counter()
{
return [counter = 0] () mutable -> int { return ++counter; };
}
Demo
Lambda expression — A lambda expression specifies an object specified inline, not just a function without a name, capable of capturing variables in scope.
Lambdas can frequently be passed around as objects.
In addition to its own function parameters, a lambda expression can refer to local variables in the scope of its definition.
Closures -
Closures are special functions that can capture the environment, i.e. variables within a lexical scope*.*
A closure is any function that closes over the environment in which it
was defined. This means that it can access variables, not in its
parameter list.
What is C++ specific part here
A closure is a general concept in programming that originated from functional programming. When we talk about the closures in C++, they always come with lambda expressions (some scholars prefer the inclusion of function object in this)
In c++ a lambda expression is the syntax used to create a special temporary object that behaves similarly to how function objects behave.
The C++ standard specifically refers to this type of object as a
closure object. This is a little bit at odds with the broader
definition of a closure, which refers to any function, anonymous or
not, that captures variables from the environment they are defined in.
As far as the standard is concerned, all instantiations of lambda expressions are closure objects, even if they don’t have any captures in their capture group.
https://pranayaggarwal25.medium.com/lambdas-closures-c-d5f16211de9a
if you want 1 2 3 4 5, you can also try this
std::function<int()> create_counter()
{
static int counter = 0;
auto f = [&] () -> int { return ++counter; };
return f;
}
If the variable is captured by vaule, then it is copy constructed from the original variable. If by reference, you can treat them as different reference to the same object.
I have a function in a class that defines a lambda and stores it in a local static variable:
class A
{
public:
void call_print()
{
static auto const print_func = [this] {
print();
};
print_func();
};
virtual void print()
{
std::cout << "A::print()\n";
}
};
class B : public A
{
public:
virtual void print() override
{
std::cout << "B::print()\n";
}
};
I also execute the following test:
int main()
{
A a;
B b;
a.call_print();
b.call_print();
}
(Live Sample)
What I expect to be printed is:
A::print()
B::print()
But what I really get is:
A::print()
A::print()
(Same object address is also printed with each)
I suspect this is due to the this capture. I assumed that it would capture the value of this when it is called, however it seems to be captured the moment the lambda is defined.
Could someone explain the semantics of lambda captures? When do they actually get provided to the function? Is it the same for all capture types, or is this a special case? Removing static fixes the problem, however in my production code I'm actually storing the lambda in a slightly-heavier object which represents a slot to which I insert into a signal later.
This has nothing to do with the semantics of lambda capture. It's simply how static works.
A static function-scoped variable is initialized exactly once. There is only ever one such object in your entire program. It will be initialized the first time the function is called (more specifically, the first time the static statement is executed). And therefore, the expression used to initialize the static variable is only ever invoked once.
So if a static function-scoped variable is initialized with data that's based on one of the function's parameters (like this), then it will only get the parameters from the first invocation of that function.
Your code creates a single lambda. It does not create different lambdas on each invocation of the function.
The behavior you seem to want is not a function-local static variable, but an object member. So just put a std::function object in the class itself, and have call_print initialize it if it is empty.
The lambda is instantiated the first time the enclosing function A::call_print() is called. Since the first time you call it on an A object, the this of that object is captured. If you reversed the order of invocation, you'd see a different result:
b.call_print();
a.call_print();
Output:
B::print()
B::print()
It more to do with the semantics of initialization of function-local static objects than those of lambda capture.
Static local variables are initialized the first time their declaration is executed.
In this case, you:
Create a lambda, capturing &A as this, that calls A.print();
Assign that lambda to print_func
Call that lambda (through print_func)
Call that lambda again.
Captured values are always captured when the lambda is created - which in this case is during the first call to call_print.
I don't think the capture is the problem here but the static keyword.
Think about your code as this:
class A
{
public:
void call_print()
{
static A* ptr = this;
ptr->print();
};
virtual void print()
{
std::cout << "A::print()\n";
}
};
class B : public A
{
public:
virtual void print() override
{
std::cout << "B::print()\n";
}
};
This is pretty much the same without a lambda.
If you look at the code it becomes pretty clear that your call a.call_print(); initializes ptr with a pointer to the object a which is then used further on.
This question is not so much about the behaviour of lambdas, but about the behaviour of static variables in functions.
The variable is created the first time code flows over it, using whatever variables are available at that time.
example:
#include <iostream>
int foo(int v)
{
static int value = v;
return v;
};
int main()
{
std::cout << foo(10) << std::endl;
std::cout << foo(11) << std::endl;
}
expected:
10
10
Because it's equivalent to:
foo::value = 10;
std::cout << foo::value << std::endl;
// foo::value = 11; (does not happen)
std::cout << foo::value << std::endl;
Indeed, the value of the capture is set when the lambda is defined, not when its called. Because you're setting a static variable to the expression that defines the lambda, this only happens the very first time the function call_print is called (by the rules governing static variables). Thus all call_print calls thereafter in fact get the same lambda, the one whose this is set to &a.