enable_shared_from_this Vs Direct Assignment - c++

Why should I use enable_shared_from_this as I can get the same effect via plain assignment also.
struct A : std::enable_shared_from_this<A> {
std::shared_ptr<A> getptr() {
return shared_from_this();
}
};
int main () {
// What is the differentce between this code
std::shared_ptr<A> p1 = make_shared<A>();
std::shared_ptr<A> p2 = p1->getptr();
// Vs this
std::shared_ptr<A> p1 = make_shared<A>();
std::shared_ptr<A> p2 = p1;
}

Because you can't get the "same" effect", at least not the one you may be thinking of.
There is no difference in the posted code methodologies, precisely because A inherits from std::enable_shared_from_this<A>. Both p1 and p2 are shared_ptr objects referring to the same concrete object (assuming only one of those sections is compiled for your tests, else you error on id name reuse).
std::enable_shared_from_this<T> allows you to acquire a std::shared_ptr<T> from some object, formally managed by some preexisting std::shared_ptr<T> of type T or derivative thereof, in locations where you have no std::shared_ptr<T> to the object to otherwise acquire, but need one for one reason or another. For example:
#include <iostream>
#include <memory>
struct A;
void foo(std::shared_ptr<A> arg)
{
}
struct A : std::enable_shared_from_this<A>
{
void method()
{
foo(shared_from_this());
}
};
int main ()
{
auto a = std::make_shared<A>();
a->method();
}
In the above example, foo requires a std::shared_ptr<A> as a parameter. From the body of A::method() no such mechanism exists without std::enable_shared_from_this<A> as a base. Without the std::enabled_shared_from_this<T> base, you would have to provide an alternative mechanism for passing the a shared pointer down the call chain until it reached foo. In short it would look something like this:
#include <iostream>
#include <memory>
struct A;
void foo(std::shared_ptr<A> arg)
{
}
struct A
{
void method(std::shared_ptr<A> me)
{
foo(me);
}
};
int main ()
{
std::shared_ptr<A> a = std::make_shared<A>();
a->method(a);
}
which is obviously dreadful and hideous. Further, there is no guarantee me in method is actually a std::shared_ptr<T> of this. Thus the standards committee blessed us with std::enable_shared_from_this<T>.

It's probably worth mentioning what shared_from_this is 'for'.
The most common use case is to 'keep myself alive' while some asynchronous process is running. A good example of this would be a completion handler, another would be a callback on 'this' when this is controlled by a shared_ptr.
for example:
#include <memory>
#include <future>
#include <thread>
#include <chrono>
#include <iostream>
using namespace std::literals;
template<class Handler>
void long_process_with_completion_handler(Handler done)
{
std::thread([done] {
std::cout << "long process starts" << std::endl;
std::this_thread::sleep_for(2000ms);
done();
}).detach();
}
struct controller : std::enable_shared_from_this<controller>
{
auto get_lock() const {
return std::unique_lock<std::mutex>(_mutex);
}
void start() {
long_process_with_completion_handler([self = shared_from_this()] {
auto lock = self->get_lock();
std::cout << "all complete" << std::endl;
});
}
mutable std::mutex _mutex;
};
int main()
{
std::condition_variable controller_done;
std::mutex done_mutex;
bool is_controller_done = 0;
// make shared controller
// start its processing
auto pcontroller = std::shared_ptr<controller>{ new controller,
[&](auto*p) {
delete p;
auto lock = std::unique_lock<std::mutex>(done_mutex);
is_controller_done = true;
std::cout << "controller destroyed" << std::endl;
lock.unlock();
controller_done.notify_all();
}};
pcontroller->start();
// destroy the controlling pointer. but our controller is still running...
pcontroller.reset();
auto lock = std::unique_lock<std::mutex>(done_mutex);
controller_done.wait(lock, [&]{ return is_controller_done;});
std::cout << "program ends" << std::endl;
}

The shared-from-this functionality enables you to obtain a shared_ptr to a shared_ptr-managed object when all you have is a raw pointer or a reference.
Just creating a shared_ptr directly from the raw pointer would create a new, unrelated reference counter.

The use case of enable_shared_from_this and shared_from_this is clear, and yet I tend to say that in most use cases it can be dropped in favor of a static method that gets a shared_ptr and then creates a new shared_ptr from it (in a very similar manner to the approach suggested by the OP, but with a static method to support the creation of the new shared_ptr).
The advantage of the static method approach is that you won't fall in the bug of trying to get shared_from_this when there is no underlying shared_ptr for this instance, resulting with bad_weak_ptr.
The disadvantage is that the API is implicitly asking the caller to come with a shared_ptr, so if the caller has just a raw pointer to an instance he can't use it (the caller may create a shared_ptr from the raw pointer and call the method, but how can he tell if the original raw pointer was not managed already by a shared_ptr?). On the other hand, if the user has in hand a unique_ptr he should be positively sure that turning it to shared_ptr in order to call the static method should be fine.
In a way the advantage and disadvantage are two sides of the same coin.
I would prefer in most cases to require the API to work with shared_ptr (it already depends on that in a way) rather than allowing working with any kind of a pointer, with the hope that there is a managed shared_ptr for it. This goes well with the advice of having APIs that cannot be easily used in a wrong way.
Here is the code presented by #RichardHodges (great example!) using the static method approach instead of using enable_shared_from_this:
// code based on Richard Hodges example
template<class Handler>
void long_process_with_completion_handler(Handler done) {
std::thread([done] {
std::cout << "long process starts" << std::endl;
std::this_thread::sleep_for(2000ms);
done();
}).detach();
}
// without the need to inherit from std::enable_shared_from_this
struct Controller {
auto get_lock() const {
return std::unique_lock<std::mutex>(_mutex);
}
static void start(std::shared_ptr<Controller>& pcontroller) {
long_process_with_completion_handler(
[self = std::shared_ptr<Controller>(pcontroller)] {
auto lock = self->get_lock();
std::cout << "all complete" << std::endl;
});
}
mutable std::mutex _mutex;
};
int main() {
std::condition_variable controller_done;
std::mutex done_mutex;
bool is_controller_done = 0;
// make shared controller and start its processing
auto pcontroller = std::shared_ptr<Controller>{ new Controller,
[&](auto*p) {
delete p;
auto lock = std::unique_lock<std::mutex>(done_mutex);
is_controller_done = true;
std::cout << "controller destroyed" << std::endl;
lock.unlock();
controller_done.notify_all();
}};
Controller::start(pcontroller);
// destroy the controlling pointer. but our controller is still running...
pcontroller.reset();
auto lock = std::unique_lock<std::mutex>(done_mutex);
controller_done.wait(lock, [&]{ return is_controller_done;});
std::cout << "program ends" << std::endl;
}
Code: http://coliru.stacked-crooked.com/a/281b0ef6d1b31c56

Related

Creating Smart pointer of the object itself (this) in its constructor

So lets say I have
class A
{
A(std::vector<std::shared_pointer<A>> &a_vec)
{
auto shared_ptr = std::make_shared<A>(*this);
a_vec.pushback<shared_ptr>;
{
};
class B
{
std::vector<std::shared_pointer<A>> a_vector_;
void constructA()
{
created_A = make_shared<A>(a_vector_);
}
}
So Im creating a method for B which creates an A and the A pushes itself to the vector B provides.
And on paper this dependancy works like I expected to, atleast I thought it was until I realised the a_vec.pushback<this*>; is not very dependable.
When I have more code inbetween the push and the shared pointer initialisation as such
A(std::vector<std::shared_pointer<A>> a_vec)
{
auto shared_ptr = std::make_shared<A>(*this);
//insert more code here
a_vec.pushback<shared_ptr>;
{
It seems that the initialisations and other stuff I do in there isn't reflected to the pointer the shared pointer is pointing. Whats the cause of this and is there a way to fix it? Also is there a reason this would be a bad practice to use?
One of the challenges when you are programming in C++ is to understand object lifetime. So it is better to make object creation and destruction as clear as possible.
As I understood your case is to memoize "automagically" all created objects. It is easier to do using "factory method" constructA
#include <iostream>
#include <vector>
#include <memory>
class A
{
public:
A() = default;
};
class B//AInstanceFactory - is a better name
{
std::vector<std::shared_ptr<A>> a_instances;
public:
void constructA()
{
a_instances.push_back(std::make_shared<A>());
}
const std::vector<std::shared_ptr<A>>& getAInstances() {
return a_instances;
}
};
int main()
{
B b;
b.constructA();
std::cout << b.getAInstances().size() << "\n";
b.constructA();
std::cout << b.getAInstances().size() << "\n";
}
[WRONG PATH]
It is possible to make object which aware of shared_ptr/weak_ptr: use template std::enable_shared_from_this.
In that case your code might be following,:
#include <iostream>
#include <vector>
#include <memory>
class A : std::enable_shared_from_this<A>
{
public:
A(std::vector<std::shared_ptr<A>>& a_vec)
{
a_vec.push_back(shared_from_this());//bad_weak_ptr here!!!!
}
};
class B
{
std::vector<std::shared_ptr<A>> a_vector_;
public:
void constructA()
{
auto a_ptr = make_shared<A>(a_vector_);
}
const std::vector<std::shared_ptr<A>>& getAVec() {
return a_vector_;
}
};
int main()
{
B b;
b.constructA();
std::cout << b.getAVec().size() << "\n";
}
BUT it wrong, because underlying weak_ptr is "ready" only after function make_shared is executed, means only after construction call.
Calling shared_from_this or weak_from_this is valid only after make_shared function is executed.

Returning std::unique_ptr to abstract type with custom deleter from a memory pool

Assume I have a templated MemoryPool class a function create(...) (which returns a pointer to a newly allocated object of type T) and a function destroy(T*) (which destroys and returns the memory back to the pool).
I would like to create a std::unique_ptr that "owns" the pointer created by the pool and returns the pointer to the pool, thus requiring a custom deleter.
The problem is, how do I make this work if the pool contains concrete objects and I want to pass around a std::unique_ptr to an abstract interface of this object.
Here is an example that doesn't compile:
#include <iostream>
#include <memory>
#include <functional>
#include <utility>
template <typename T>
class MemoryPool {
public:
template <typename ... ARGS>
T* create(ARGS&&... args) {
std::cout << "MemoryPool::create()" << std::endl;
return new T(std::forward<ARGS>(args)...);
}
void destroy(T* ptr) {
std::cout << "MemoryPool::destroy()" << std::endl;
delete ptr;
}
};
class ITest {
public:
ITest() {
std::cout << "ITest::ITest()" << std::endl;
}
virtual ~ITest() {
std::cout << "ITest::~ITest()" << std::endl;
}
virtual void sayHello() = 0;
};
class Test :public ITest {
public:
Test() {
std::cout << "Test::Test()" << std::endl;
}
~Test() {
std::cout << "Test::~Test()" << std::endl;
}
void sayHello() override {
std::cout << "Test says hello" << std::endl;
}
};
class ITestOwner {
public:
ITestOwner(std::unique_ptr<ITest> ptr) :
_ptr(std::move(ptr))
{
std::cout << "ITestOwner::ITestOwner()" << std::endl;
}
~ITestOwner() {
std::cout << "ITestOwner::~ITestOwner()" << std::endl;
}
void sayHello() { _ptr->sayHello(); }
private:
std::unique_ptr<ITest> _ptr;
};
int main() {
MemoryPool<Test> pool;
std::unique_ptr<Test, std::function<void(Test*)>> ptr(pool.create(), [&pool](Test* ptr){
std::cout << "Custom Deleter" << std::endl;
pool.destroy(ptr);
});
ITestOwner owner(std::move(ptr));
owner.sayHello();
return 0;
}
Keep in mind that, my real MemoryPool class would actually act as a normal memory pool and not use new/delete as I have done.
In this example, ITestOwner should take over ownership of the std::unique_ptr to a ITest abstract object. Then, when ITestOwner is destroyed, the smart pointer will be destroyed, and the Test object should be returned to the memory pool.
Is there a way to accomplish this?
The code doesn't compile because std::unique_ptr<ITest> is used by ITestOwner while you to forward it std::unique_ptr<Test, std::function<void(Test*)>>. It obviously doesn't compile because std::unique_ptr<ITest> calls delete on ITest instead of calling some complex arbitrary function.
You'd need to use unique_ptr<ITest, function<void(ITest*)>> for it to work and in addition add some unsighty conversion code from function<void(Test*)> to function<void(ITest*)>... I'd say this is simply not good. unique_ptr is designed to be simple and efficient - the destructor is supposed to wrap basic functionality but it isn't convenient enough for complicated purposes.
Basically, unique_ptr is not designed for this task. It is supposed to be lightweight and you already use heavy functionality like std::function that ruins the whole purpose. Instead, you can use shared_ptr which type erases the deleter and hides it - so you'd need nothing to worry about. If you want to still restrict user to unique ownership you can surely find other 3rd party open source libraries that implement the smart pointer you want - there are lots of them.

Check validity of std::function before calling?

I'm trying to program a simple but flexible event system (mostly just as an exercise, I know there are existing libraries that have really good event handlers), and I've run into a little stumbling block.
How can you check if an std::function that's a delegate (probably through a lambda, possibly though std::bind) is a valid function/if the object for the member function still exists before calling it? I've tried simply using std::function's bool operator, but haven't had any success.
Ideally I'd like to A. do the checking somewhere other than inside the delegate function, and B. still have the code be valid when the std::function that's being checked isn't a delegate.
Any ideas?
Edit: Here's the source for the test that I ran
#include <iostream>
#include <string>
#include <functional>
class Obj {
public:
std::string foo;
Obj(std::string foo) : foo(foo) {}
std::function<void()> getDelegate() {
auto callback = [this]() {this->delegatedFn();};
return callback;
}
void delegatedFn() {
std::cout << foo << std::endl;
}
};
int main() {
Obj* obj = new Obj("bar");
std::function<void()> callback = obj->getDelegate();
callback();
delete obj;
//perform some type of check here whether function is valid, without needing to know whether the function is a delegate or not
if(callback) {
std::cout << "Callback is valid" << std::endl; //callback is still considered valid after obj is deleted
callback(); //no exception thrown, prints a random series of characters
}
else {
std::cout << "Callback is invalid" << std::endl;
}
return 0;
}
You can use smart pointers (std::shared_ptr/std::weak_ptr) instead of naked ones:
#include <iostream>
#include <string>
#include <functional>
#include <memory>
class Obj {
public:
std::string foo;
Obj(std::string foo) : foo(foo) {}
void delegatedFn() {
std::cout << foo << std::endl;
}
};
int main() {
auto obj = std::make_shared<Obj>("bar");
std::weak_ptr<Obj> ptr = obj;
std::function<void()> callback = [ptr](){
auto sh = ptr.lock();
if(sh) { std::cout << "valid" << std::endl; sh->delegatedFn(); }
else { std::cout << "invalid" << std::endl; }
};
callback();
obj = nullptr;
callback();
return 0;
}
In this case you are not directly checking the validity of a std::function (that is valid when you assign it something, even if that something captures a dangling pointer).
Instead, you check that the referred object is still alive from within the function itself.
The broadcaster/listener pattern I use looks like this:
template<class...Args>
struct broadcaster {
std::vector< std::weak_ptr< std::function<void(Args...)> > > callbacks;
void operator()(Args...args) const {
std::remove_erase_if( begin(callbacks), end(callbacks), [](auto&& ptr){return !ptr;} );
auto tmp = callbacks;
for (auto pf : tmp) {
if (pf && *pf) (*pf)(args...);
}
}
std::shared_ptr<void> listen( std::shared_ptr<std::function<void(Args...)>> f ) {
callbacks.push_back(f);
return f;
}
std::shared_ptr<void> listen( std::function<void(Args...)> f ) {
auto ptr = std::make_shared<std::function<void(Args...)>>(std::move(f));
return listen(ptr);
}
};
Listeners to a message .listen their callback with broadcaster. They get back a shared_ptr<void> token.
So long as that token exists, the broadcaster will send messages at the function object passed in.
Obj would either store a std::vector<std::shared_ptr<void>> tokens or a single std::shared_ptr<void>. When it was destroyed, its listeners would automatically deregister.
Alternatively, Obj could inherit from shared_from_this. Then it implements
std::function<void()> delegate;
std::shared_ptr<std::function<void()>> getDelegatedFn() {
if (!delegate) delegate = [this]{ this->delegateFn(); }
return {
&delegate,
shared_from_this()
};
}
which shares the lifetime of the Obj instance itself (uses the aliasing constructor of shared_ptr). Pass this to listen and done.

Multiple shared_ptr storing same pointer

Consider this program:
#include <memory>
#include <iostream>
class X
: public std::enable_shared_from_this<X>
{
public:
struct Cleanup1 { void operator()(X*) const; };
struct Cleanup2 { void operator()(X*) const; };
std::shared_ptr<X> lock1();
std::shared_ptr<X> lock2();
};
std::shared_ptr<X> X::lock1()
{
std::cout << "Resource 1 locked" << std::endl;
return std::shared_ptr<X>(this, Cleanup1());
}
std::shared_ptr<X> X::lock2()
{
std::cout << "Resource 2 locked" << std::endl;
return std::shared_ptr<X>(this, Cleanup2());
}
void X::Cleanup1::operator()(X*) const
{
std::cout << "Resource 1 unlocked" << std::endl;
}
void X::Cleanup2::operator()(X*) const
{
std::cout << "Resource 2 unlocked" << std::endl;
}
int main()
{
std::cout << std::boolalpha;
X x;
std::shared_ptr<X> p1 = x.lock1();
{
std::shared_ptr<X> p2 = x.lock2();
}
}
I don't see anything in the C++11 Standard section 20.7.2 suggesting any of this is invalid. It's a bit unusual to have two shared_ptr objects store the same pointer &x but not share ownership, and to use "deleters" that do not end the lifetime of *get(), but nothing forbids it. (And if either of those are entirely unintended, it would be difficult to explain why some shared_ptr member functions accept a std::nullptr_t value.) And as expected, the program outputs:
Resource 1 locked
Resource 2 locked
Resource 2 unlocked
Resource 1 unlocked
But now if I add a bit to main():
int main()
{
std::cout << std::boolalpha;
X x;
std::shared_ptr<X> p1 = x.lock1();
bool test1( x.shared_from_this() );
std::cout << "x.shared_from_this() not empty: " << test1 << std::endl;
{
std::shared_ptr<X> p2 = x.lock2();
}
try {
bool test2( x.shared_from_this() );
std::cout << "x.shared_from_this() not empty: " << test2 << std::endl;
} catch (std::exception& e) {
std::cout << "caught: " << e.what() << std::endl;
}
}
then things get trickier. With g++ 4.6.3, I get the output:
Resource 1 locked
x.shared_from_this() not empty: true
Resource 2 locked
Resource 2 unlocked
caught: std::bad_weak_ptr
Resource 1 unlocked
Why would the second call to shared_from_this() fail? All the requirements of 20.7.2.4p7 are met:
Requires: enable_shared_from_this<T> shall be an accessible base class of T. *this shall be a subobject of an object t of type T. There shall be at least one shared_ptr instance p that owns &t.
[T is X, t is x, p is p1.]
But g++'s enable_shared_from_this essentially follows the suggested implementation from the (non-normative) "Note" in 20.7.2.4p10, using a private weak_ptr member in class enable_shared_from_this. And it seems impossible to account for this sort of issue without doing something considerably more complicated in enable_shared_from_this.
Is this a defect in the Standard? (If so, no comment is needed here on what the solution "should" be: add a requirement so the example program invokes Undefined Behavior, change the Note to not suggest such a simple implementation would be sufficient,....)
Yes, there is a defect here in C++11. In allowing this:
It's a bit unusual to have two shared_ptr objects store the same pointer &x but not share ownership, and to use "deleters" that do not end the lifetime of *get(), but nothing forbids it.
This should be explicitly stated to be undefined behavior, regardless of what the "deleters" do. Sure, it may be technically not illegal to do things that way.
However, you are lying to people who use the code. The expectation of anyone who receives a shared_ptr is that they now have ownership of the object. So long as they keep that shared_ptr (or a copy thereof) around, the object it points to will still exists.
That is not the case with your code. So I would say that it is syntactically correct but semantically invalid.
The language for shared_from_this is fine. It's the language for shared_ptr that needs changing. It should state that it is undefined behavior to create two separate unique pointers that "own" the same pointer.
I agree this is a hole in the specification, thus a defect. It's basically the same as http://open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2179 although that issue comes at it from a slightly different (and IMHO more obviously broken) angle.
I'm not sure I agree that this is a misuse of shared_ptr, I think it's fine to do that with shared_ptrs, because unlike the code in issue 2179 you use no-op deleters. I think the problem is when you try to combine that kind of use of shared_ptr with enable_shared_from_this.
So my first thought was to fix it by extending the requirements of shared_from_this:
Requires: enable_shared_from_this<T> shall be an accessible base class of T. *this shall be a subobject of an object t of type T. There shall be at least one shared_ptr instance p that owns &t and any other shared_ptr instances that own &t shall share ownership with p.
This isn't quite sufficient though, because your example meets that requirement: at the second call to shared_from_this() there is only one owner (p1) but you've already "corrupted" the state of the enable_shared_from_this base class by calling lock2().
A smaller form of the program is:
#include <memory>
using namespace std;
int main()
{
struct X : public enable_shared_from_this<X> { };
auto xraw = new X;
shared_ptr<X> xp1(xraw); // #1
{
shared_ptr<X> xp2(xraw, [](void*) { }); // #2
}
xraw->shared_from_this(); // #3
}
All three of libstdc++, libc++ and VC++ (Dinkumware) behave the same and throw bad_weak_ptr at #3, because at #2 they update the weak_ptr<X> member of the base class to make it share ownership with xp2, which goes out of scope leaving the weak_ptr<X> in the expired state.
Interestingly boost::shared_ptr doesn't throw, instead #2 is a no-op and #3 returns a shared_ptr that shares ownership with xp1. This was done in response to a bug report with almost exactly the same example as the one above.
This issue and other related ones were clarified in C++17. Now std::enable_shared_from_this<T> is specified as if having a single std::weak_ptr<T> weak_this; member. For a non-array specialization of std::shared_ptr, that member is assigned by std::shared_ptr constructors, std::make_shared, and std::allocate_shared as described in [util.smartptr.shared.const]/1:
Enables shared_­from_­this with p, for a pointer p of type Y*, means that if Y has an unambiguous and accessible base class that is a specialization of enable_­shared_­from_­this, then remove_­cv_­t<Y>* shall be implicitly convertible to T* and the constructor evaluates the statement:
if (p != nullptr && p->weak_this.expired())
p->weak_this = shared_ptr<remove_cv_t<Y>>(*this, const_cast<remove_cv_t<Y>*>(p));
So the correct behavior of the second main in my OP is now that no exception will be thrown and both "not empty" checks will show true. Since at the call to lock2() the internal weak_ptr is already owned and therefore not expired(), lock2() leaves the weak_ptr unchanged, and so the second call to shared_from_this() returns a shared_ptr which shared ownership with p1.
X x;
std::shared_ptr<X> p1 = x.lock1();
(...sniped...)
}
Such code is breaks the semantics of "owning" "smart pointers":
they can be copied
as long as one copy is kept around, the owned object is kept around
This invariant is so essential, I'd argue that such practice should be rejected by code review. But there is a variant of what you suggest that satisfies the invariant:
the object must be dynamically managed (so, not automatic)
any family of owning objects has shared ownership of the dynamically managed object
each member of a family has shared ownership of the "deleter" of that family
So here we have shared owning objects that are part of different "families" of owning objects, they aren't "equivalent" as they have different:
"deleters" object
use_count() values
control blocks
owner_before results
but they all prevent the destruction of the same object; this is done by keeping a copy of the shared_ptr in each and every "deleter" object.
A clean replacement for std::shared_from_this is used to have complete control over the initialization of the std::weak_ptr<T> member.
#include <memory>
#include <iostream>
#include <cassert>
// essentially like std::shared_from_this
// unlike std::shared_from_this the initialization IS NOT implicit
// calling set_owner forces YOU to THINK about what you are doing!
template <typename T>
class my_shared_from_this
{
std::weak_ptr<T> weak;
public:
void set_owner(std::shared_ptr<T>);
std::shared_ptr<T> shared_from_this() const;
};
// shall be called exactly once
template <typename T>
void my_shared_from_this<T>::set_owner(std::shared_ptr<T> shared)
{
assert (weak.expired());
weak = shared;
}
template <typename T>
std::shared_ptr<T> my_shared_from_this<T>::shared_from_this() const
{
assert (!weak.expired());
return weak.lock();
}
class X : public my_shared_from_this<X>
{
public:
struct Cleanup1 {
std::shared_ptr<X> own;
Cleanup1 (std::shared_ptr<X> own) : own(own) {}
void operator()(X*) const;
};
struct Cleanup2 {
std::shared_ptr<X> own;
Cleanup2 (std::shared_ptr<X> own) : own(own) {}
void operator()(X*) const;
};
std::shared_ptr<X> lock1();
std::shared_ptr<X> lock2();
X();
~X();
};
// new shared owner family with shared ownership with the other ones
std::shared_ptr<X> X::lock1()
{
std::cout << "Resource 1 locked" << std::endl;
// do NOT call set_owner here!!!
return std::shared_ptr<X>(this, Cleanup1(shared_from_this()));
}
std::shared_ptr<X> X::lock2()
{
std::cout << "Resource 2 locked" << std::endl;
return std::shared_ptr<X>(this, Cleanup2(shared_from_this()));
}
void X::Cleanup1::operator()(X*) const
{
std::cout << "Resource 1 unlocked" << std::endl;
}
void X::Cleanup2::operator()(X*) const
{
std::cout << "Resource 2 unlocked" << std::endl;
}
X::X()
{
std::cout << "X()" << std::endl;
}
X::~X()
{
std::cout << "~X()" << std::endl;
}
// exposes construction and destruction of global vars
struct GlobDest {
int id;
explicit GlobDest(int id);
~GlobDest();
};
GlobDest::GlobDest(int id)
: id(id)
{
std::cout << "construction of glob_dest #" << id << std::endl;
}
GlobDest::~GlobDest() {
std::cout << "destruction of glob_dest #" << id << std::endl;
}
GlobDest glob_dest0 {0};
std::shared_ptr<X> glob;
GlobDest glob_dest1 {1};
std::shared_ptr<X> make_shared_X()
{
std::cout << "make_shared_X" << std::endl;
std::shared_ptr<X> p = std::make_shared<X>();
p->set_owner(p);
return p;
}
int test()
{
std::cout << std::boolalpha;
std::shared_ptr<X> p = make_shared_X();
static std::shared_ptr<X> stat;
{
std::shared_ptr<X> p1 = p->lock1();
stat = p1;
{
std::shared_ptr<X> p2 = p->lock2();
glob = p2;
std::cout << "exit scope of p2" << std::endl;
}
std::cout << "exit scope of p1" << std::endl;
}
std::cout << "exit scope of p" << std::endl;
}
int main()
{
test();
std::cout << "exit main" << std::endl;
}
Output:
construction of glob_dest #0
construction of glob_dest #1
make_shared_X
X()
Resource 1 locked
Resource 2 locked
exit scope of p2
exit scope of p1
exit scope of p
exit main
Resource 1 unlocked
destruction of glob_dest #1
Resource 2 unlocked
~X()
destruction of glob_dest #0

Is there a way to change the delete action on an existing instance of shared_ptr

I have a function where I want a cleanup action done 90% of the time, but in 10% I want some other action to be done.
Is there some way to use some standard scoped control likeshared_ptr<> so that initially it can have one delete action and then later in the function the delete action can be changed?
shared_ptr<T> ptr( new T, std::mem_fun_ref(&T::deleteMe) );
ptr.pn.d = std::mem_fun_ref(&T::queueMe);
Not really - the standard for shared_ptr is written in such a way that the Deleter may be stored by value in control node (a special object that contains the reference counter, holds deleter, tracks weak pointers etc). The deleter is type-erased, but if you know the concrete deleter type somehow, you can use std::get_deleter<Deleter>(ptr). With it you may access the deleter and change its state. Example:
struct A {};
struct deleter {
void operator()(A* a) {delete a; }
int m_state;
};
std::shared_ptr<A> ptr(new A(), deleter{});
std::get_deleter<deleter>(ptr)->m_state = 5;
And if you use just a function pointer for all deleters, then yes you can completely replace it, as all potential deleters use the same signature.
(Yes I know the question is 9 years old, but I've just faced this problem in 2020 and solved it like this. The possible reason for it is wrapping C pointers and objects from legacy code that manage ownership through raw pointers)
I don't think you can change the deleter once the shared_ptr was created.
But why would you do that ? Usually, when you create an object, you know immediatly how it must be destroyed. This is not likely to change.
If you really must do some specific treatments, you still can provide a custom deleter which does special things depending on the required logic.
There is a valid reason to need to change the deleter. Take this for example:
int foo( std::shared_ptr<double>& bar ) {
...
std::shared_ptr<double> p( my_allocator<double>::allocate(), my_deleter<double>() );
bar.swap(p); // this copies the deleter
...
}
int main( int, char** ) {
std::shared_ptr<double> d;
foo( d ); // d now has a new deleter that will be called when it goes out of scope
...
}
In this case the foo() function allocates a double* using some special allocator. It needs to free that memory in a special way also. The caller shouldn't need to know how to free the memory.
#include <iostream>
#include <memory>
#include <functional>
struct A {
~A() {
std::cout << "~A()" << std::endl;
}
};
using DeleterCb = std::function<void(A* p)>;
struct ADeleter {
public:
explicit ADeleter(DeleterCb cb) :
mDeleterCb(cb) {}
ADeleter() = delete;
~ADeleter() = default;
void operator()(A *a) {
mDeleterCb(a);
}
void setDeleterCb(DeleterCb cb) {
mDeleterCb = cb;
}
private:
DeleterCb mDeleterCb;
};
int main() {
auto sp = std::shared_ptr<A>(new A{},
ADeleter([](A *p){
delete p;
std::cout << "deleter_1" << std::endl;
})
);
std::get_deleter<ADeleter>(sp)->setDeleterCb(
[](A *p){
delete p;
std::cout << "deleter_2" << std::endl;
}
);
}
This doesn't make any sense, since there is any number of shared_ptrs managing the ownership of the value. You'd need to modify them all, and that's not feasible. Let's not forget that a control block is an implementation detail, so going "aha, but change it in the control block" won't work.
The delete actions should be controlled by the instance owned by shared_ptr, e.g.
class C {
...
void (C::action*)() { &C::action1 };
void action1();
void action2();
~C() { (this->*action)(); }
};
void test() {
std::shared_ptr<C> a;
a->action = &C::action2;
// action2 gets invoked once `a` falls out of scope
}