So in modern C++ we use smart pointers to make sure that a shared resource didn't get deleted before we go to use it.
Is there an equivalent for std::function<> for lambda callbacks? Where you pass out a function of a certain signature rather than a whole class or interface, general delegate injection.
Example code of problem:
struct Foo{
int i = 0;
void addInt(int input){
i += input;
}
};
int main(int argc, char* args[]){
Foo* lostMemory = new Foo();
std::function<void(int)> lambda = [lostMemory](int b) { lostMemory->addInt(b); };
//run memory good
lambda(8);
//run memory deleted - should crash right?
delete lostMemory;
lambda(8);
return 0;
}
https://godbolt.org/z/qsK65ozGG
The fact it didn't crash isn't the problem or question
Question: What is the right way to create a std::functional like this, where the resource may be deleted?
Note: in an example where lostMemory will be an instance of a class passing itself, and can't just be changed to a std::shared_ptr<>. Stack overflow simplification makes it look like that is the answer.
Why don't you just use smart pointers as you mentioned?
#include <functional>
#include <memory>
#include <iostream>
struct Foo{
Foo() {
std::cout << "Foo created" << std::endl;
}
~Foo() {
std::cout << "Foo destroyed" << std::endl;
}
int i = 0;
void addInt(int input){
i += input;
}
};
int main(int argc, char* args[]){
std::cout << "-- Start of program" << std::endl;
std::shared_ptr<Foo> lostMemory( new Foo() );
std::weak_ptr<Foo> weak = lostMemory;
std::cout << "-- Before lambda" << std::endl;
std::function<void(int)> lambda = [weak](int b) {
if ( auto sp = weak.lock() ) {
std::cout << "Added int" << std::endl;
sp->addInt(b);
}
else std::cout << "Not doing this" << std::endl;
};
std::cout << "-- After lambda" << std::endl;
lambda(8);
std::cout << "-- Before deleting" << std::endl;
lostMemory.reset();
std::cout << "-- After lambda" << std::endl;
lambda(8);
std::cout << "-- The end" << std::endl;
return 0;
}
Produces
Program stdout
-- Start of program
Foo created
-- Before lambda
-- After lambda
Added int
-- Before deleting
Foo destroyed
-- After lambda
Not doing this
-- The end
Godbolt link: https://godbolt.org/z/ceMevznhv
See, lambdas are mere anonymous functors under the hood. If you paste the above code on cppinsights.io you will have something like this (cleaned up a bit):
#include <functional>
#include <memory>
struct Foo
{
int i = 0;
inline void addInt(int input)
{
this->i = this->i + input;
}
};
int main(int argc, char ** args)
{
std::shared_ptr<Foo> lostMemory = std::shared_ptr<Foo>(new Foo());
class __lambda_13_39
{
public:
inline /*constexpr */ void operator()(int b) const
{
static_cast<const std::__shared_ptr_access<Foo, 2, false, false>&>(lostMemory).operator->()->addInt(b);
}
private:
std::shared_ptr<Foo> lostMemory;
public:
__lambda_13_39(const std::shared_ptr<Foo> & _lostMemory)
: lostMemory{_lostMemory}
{}
};
std::function<void (int)> lambda = std::function<void (int)>(__lambda_13_39{lostMemory});
lambda.operator()(8);
static_cast<std::__shared_ptr<Foo, 2>&>(lostMemory).reset();
lambda.operator()(8);
return 0;
}
You can see that the functor holds an std::shared_ptr<Foo>
as a member. That is exactly what the lambda will do as well.
Alternatively, use a functor itself instead of a lambda for cases that are a bit more evolving.