In the following code, we are creating an object, bind one function and call it before then after deleting the object.
This obviously leads to a segmentation fault as the underlying object was used after deletion.
In the context of a library providing callbacks for asynchronous data, how are we supposed to prevent callback functions to point to a nullptr
?
You can test at cpp.sh/5ubbg
#include <memory>
#include <functional>
#include <iostream>
class CallbackContainer {
public:
std::string data_;
CallbackContainer(std::string data): data_(data) {}
~CallbackContainer() {}
void rawTest(const std::string& some_data);
};
void CallbackContainer::rawTest(const std::string& some_data) {
std::cout << data_ << " " << some_data << std::endl;
}
int main(int /* argc */, char const** /* argv */) {
std::unique_ptr<CallbackContainer> container;
container.reset(new CallbackContainer("Internal data"));
auto callback = std::bind(&CallbackContainer::rawTest, container.get(), std::placeholders::_1);
callback("Before");
std::cout << &callback << std::endl;
container.reset();
std::cout << &callback << std::endl;
callback("After");
return 0;
}
Returns:
> Internal data Before
> 0x7178a3bf6570
> 0x7178a3bf6570
> Error launching program (Segmentation fault)
If you can share ownership, do this:
int main(int /* argc */, char const** /* argv */) {
std::shared_ptr<CallbackContainer> container; // shared pointer
container.reset(new CallbackContainer("Internal data"));
// shared with functor
auto callback = std::bind(&CallbackContainer::rawTest, container, std::placeholders::_1);
callback("Before");
std::cout << &callback << std::endl;
container.reset();
std::cout << &callback << std::endl;
callback("After");
return 0;
}
If not, you should somehow pass invalidity to function object explicitly. This assumes that you know when container is deleted, and manually invalidate explicitly before that:
int main(int /* argc */, char const** /* argv */) {
std::unique_ptr<CallbackContainer> container;
container.reset(new CallbackContainer("Internal data"));
std::atomic<CallbackContainer*> container_raw(container.get());
auto callback = [&container_raw] (std::string data)
{
if (auto c = container_raw.load())
c->rawTest(data);
};
callback("Before");
std::cout << &callback << std::endl;
container_raw.store(nullptr);
container.reset();
std::cout << &callback << std::endl;
callback("After");
return 0;
}
For asio cases, usually shared_from_this()
is used, like std::bind(&MyClass::MyMemFunc, shared_from_this(), ptr);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With