Discussion:
using weak_ptr and shared_ptr for singleton
Stephan Born
2002-10-07 15:39:36 UTC
Permalink
Hello!

I do some experiments on shared_ptr and weak_ptr. So I tried to use them
for a singleton-class, which does not need an explicit
singleton::release_instance method. The singleton-instance will be
destroyed everytime the last external shared_ptr holding the instance
goes out of scope. Is this a valid usage for weak_ptr? Are there any
traps I do not see using these smart_ptrs in this context?

The drawback of using shared_ptr with singleton is the need for a public
destructor. But this is dangerous....it is possible to write

singleton::singleton_ptr first_ptr = singleton::instance();
delete first_ptr.get();

Declaring the destructor as private is only possible when defining
boost::checked_delete as friend of the singleton-class.
( there is a thread on comp.lang.c++.moderated with the topic
'specialization of function template as friend with MSVC6SP5 not
possible' which deals with this problem of friendship... )
But then it is still possible to write the following code

singleton::singleton_ptr first_ptr = singleton::instance();
boost:checked_delete( first_ptr.get() );

OK, it's very unlikely to do it by chance, but it is possible.

Is there a way to allow destruction of the instance only to shared_ptr?


<code>

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/utility.hpp>

class singleton : boost::noncopyable
{
public:
typedef boost::shared_ptr< singleton > singleton_ptr;

static singleton_ptr instance()
{
singleton_ptr tmp;

if( s_instance.expired() )
{
tmp.reset( new singleton );
s_instance = tmp;
}
else
{
tmp = boost::make_shared( s_instance );
}

return tmp;
}

void foo()
{
// do something
i++;
}

// like to make it private in order to prohibit delete on it,
// but boost::shared_ptr<> can't handle it....so sad :(
~singleton(){};

private:
typedef boost::weak_ptr< singleton > internal_singleton_ptr;

singleton(){};
int i;

static internal_singleton_ptr s_instance;
};

singleton::internal_singleton_ptr singleton::s_instance;


int main()
{
{
singleton::singleton_ptr first_ptr = singleton::instance();

{
singleton::singleton_ptr second_ptr = singleton::instance();
second_ptr->foo();
}

first_ptr->foo();
}

singleton::singleton_ptr another_ptr = singleton::instance();

another_ptr->foo();

return 0;
}


</code>

Regards, Stephan
--
---------------------------------------------------------------
Dipl.-Inf. (FH) Stephan Born | beusen Solutions GmbH
fon: +49 30 549932-0 | Landsberger Allee 366
fax: +49 30 549932-21 | 12681 Berlin
mailto:***@beusen.de | Germany
---------------------------------------------------------------
PGP-Key verfügbar | PGP-Key available
---------------------------------------------------------------
Nicola Musatti
2002-10-08 13:06:36 UTC
Permalink
Post by Stephan Born
Hello!
I do some experiments on shared_ptr and weak_ptr. So I tried to use them
for a singleton-class, which does not need an explicit
singleton::release_instance method. The singleton-instance will be
destroyed everytime the last external shared_ptr holding the instance
goes out of scope. Is this a valid usage for weak_ptr? Are there any
traps I do not see using these smart_ptrs in this context?
Within the singleton class I hold the instance with a shared_ptr. I
consider this the best option, as it guarantees the correct order of
destruction when a singleton depends on another one.
Post by Stephan Born
The drawback of using shared_ptr with singleton is the need for a public
destructor. But this is dangerous....it is possible to write
singleton::singleton_ptr first_ptr = singleton::instance();
delete first_ptr.get();
You should rely on the constructor that takes an additional deleter
argument, something like (not compiled and almost certainly incomplete
and incorrect):

class SingletonDeleter {
friend class shared_ptr<Singleton>;
operator() (Singleton *p) { delete p; }
};

class Singleton {
friend class SingletonDeleter;
public:
shared_ptr<Singleton,SingletonDeleter> instance() {
if ( inst == 0 )
inst = shared_ptr<Singleton,SingletonDeleter> (
new Singleton,SingletonDeleter());
return inst;
}
};

typedef shared_ptr<Singleton,SingletonDeleter> SingletonPtr;
Post by Stephan Born
Declaring the destructor as private is only possible when defining
boost::checked_delete as friend of the singleton-class.
[...]
Post by Stephan Born
But then it is still possible to write the following code
singleton::singleton_ptr first_ptr = singleton::instance();
boost:checked_delete( first_ptr.get() );
OK, it's very unlikely to do it by chance, but it is possible.
Moreover, you rely on what should be an implementation detail.

Cheers,
Nicola Musatti
Stephan Born
2002-10-08 16:14:20 UTC
Permalink
Post by Nicola Musatti
Within the singleton class I hold the instance with a shared_ptr. I
consider this the best option, as it guarantees the correct order of
destruction when a singleton depends on another one.
But I *want* it to be delete automagically...and I do not see the
danger: if there is a dependent second_singleton it would hold a
shared_ptr to my singleton, and singleton would not be destroyed before
second_singleton is destroyed by its own. Maybe it should be made an
option by using policies....but I really do not the danger.
Post by Nicola Musatti
You should rely on the constructor that takes an additional deleter
argument, something like (not compiled and almost certainly incomplete
class SingletonDeleter {
friend class shared_ptr<Singleton>;
operator() (Singleton *p) { delete p; }
};
class Singleton {
friend class SingletonDeleter;
shared_ptr<Singleton,SingletonDeleter> instance() {
if ( inst == 0 )
inst = shared_ptr<Singleton,SingletonDeleter> (
new Singleton,SingletonDeleter());
return inst;
}
};
typedef shared_ptr<Singleton,SingletonDeleter> SingletonPtr;
Yes, I fear it *is* incorrect ;) look at the bottom of the posting for
my implelentation
Post by Nicola Musatti
Post by Stephan Born
Declaring the destructor as private is only possible when defining
boost::checked_delete as friend of the singleton-class.
[...]
Post by Stephan Born
But then it is still possible to write the following code
singleton::singleton_ptr first_ptr = singleton::instance();
boost:checked_delete( first_ptr.get() );
OK, it's very unlikely to do it by chance, but it is possible.
Moreover, you rely on what should be an implementation detail.
OK, I hope that this will be the correct way:
* I do not rely on implementation-detail ( make boost::checked_delete a
friend )
* I provide my own deleter-functor

<code>

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/utility.hpp>

class singleton : boost::noncopyable
{
public:
typedef boost::shared_ptr< singleton > singleton_ptr;

static singleton_ptr instance()
{
singleton_ptr tmp;

if( s_instance.expired() )
{
tmp.reset( new singleton, checked_deleter() ); // <-- NEW!!!
s_instance = tmp;
}
else
{
tmp = boost::make_shared( s_instance );
}

return tmp;
}

void foo()
{
// do something
i++;
}

private:
// NEW !!!!
struct checked_deleter
{
operator() ( singleton * p_ )
{
// does the same as boost::checked_delete from BOOST 1.28.0
typedef char type_must_be_complete[ sizeof( p_ ) ];
delete p_;
}
};
friend struct checked_deleter;

typedef boost::weak_ptr< singleton > internal_singleton_ptr;

singleton(){};
~singleton(){}; // NOW IT'S PRIVATE !!!

int i;

static internal_singleton_ptr s_instance;
};

singleton::internal_singleton_ptr singleton::s_instance;


int main()
{
{
singleton::singleton_ptr first_ptr = singleton::instance();

{
singleton::singleton_ptr second_ptr = singleton::instance();
second_ptr->foo();
}

first_ptr->foo();
}

singleton::singleton_ptr another_ptr = singleton::instance();


// OK, these things do not work anymore.....
//delete( another_ptr.get() );
//boost::checked_delete( another_ptr.get() );
//singleton::singleton_deleter deleter();
//deleter( another_ptr.get() );

another_ptr->foo();

return 0;
}

</code>
--
---------------------------------------------------------------
Dipl.-Inf. (FH) Stephan Born | beusen Solutions GmbH
fon: +49 30 549932-0 | Landsberger Allee 366
fax: +49 30 549932-21 | 12681 Berlin
mailto:***@beusen.de | Germany
---------------------------------------------------------------
PGP-Key verfügbar | PGP-Key available
---------------------------------------------------------------
Nicola Musatti
2002-10-09 14:01:29 UTC
Permalink
Post by Stephan Born
Post by Nicola Musatti
Within the singleton class I hold the instance with a shared_ptr. I
consider this the best option, as it guarantees the correct order of
destruction when a singleton depends on another one.
But I *want* it to be delete automagically...and I do not see the
danger: if there is a dependent second_singleton it would hold a
shared_ptr to my singleton, and singleton would not be destroyed before
second_singleton is destroyed by its own. Maybe it should be made an
option by using policies....but I really do not the danger.
I think we mean the same thing. The only thing I was pointing out is
that my static data member that holds the pointer is a shared_ptr and
not a weak_ptr. This is because I want my singleton instance to survive
even if sometime during execution it isn't used by other objects, rather
than be recreated each time as yours is.

Cheers,
Nicola Musatti

Peter Dimov
2002-10-08 12:55:02 UTC
Permalink
Post by Stephan Born
Hello!
I do some experiments on shared_ptr and weak_ptr. So I tried to use them
for a singleton-class, which does not need an explicit
singleton::release_instance method. The singleton-instance will be
destroyed everytime the last external shared_ptr holding the instance
goes out of scope. Is this a valid usage for weak_ptr? Are there any
traps I do not see using these smart_ptrs in this context?
The drawback of using shared_ptr with singleton is the need for a public
destructor. But this is dangerous....it is possible to write
singleton::singleton_ptr first_ptr = singleton::instance();
delete first_ptr.get();
Declaring the destructor as private is only possible when defining
boost::checked_delete as friend of the singleton-class.
( there is a thread on comp.lang.c++.moderated with the topic
'specialization of function template as friend with MSVC6SP5 not
possible' which deals with this problem of friendship... )
But then it is still possible to write the following code
singleton::singleton_ptr first_ptr = singleton::instance();
boost:checked_delete( first_ptr.get() );
OK, it's very unlikely to do it by chance, but it is possible.
Is there a way to allow destruction of the instance only to shared_ptr?
Your class isn't a "canonical" singleton since those have one instance per
program; yours has one instance at a time. That aside, there are two ways to
prohibit deletion:

1. Make the destructor protected and return an instance of a derived class:

X.hpp:

class X
{
protected:

~X();

public:

shared_ptr<X> instance();
};

X.cpp:

class X_impl: public X // not visible to clients
{
};

shared_ptr<X> X::instance()
{
return shared_ptr<X>(new X_impl);
}

2. Use a custom private deallocator:

class X
{
private:

~X();
static void destroy(X * p) { delete p; }

public:

shared_ptr<X> instance()
{
return shared_ptr<X>(new X, X::destroy);
}
};
Peter Dimov
2002-10-08 13:57:31 UTC
Permalink
From: "Stephan Born" <yg-boost-***@gmane.org>
[...]
Post by Stephan Born
Declaring the destructor as private is only possible when defining
boost::checked_delete as friend of the singleton-class.
( there is a thread on comp.lang.c++.moderated with the topic
'specialization of function template as friend with MSVC6SP5 not
possible' which deals with this problem of friendship... )
I missed that thread... no 'boost', 'shared_ptr' or 'checked_delete' in the
subject. :-)

Anyway, making boost::checked_delete a friend - even if you could do it
;-) - is not guaranteed to work. boost::shared_ptr isn't required to use
boost::checked_delete.
Stephan Born
2002-10-08 14:32:10 UTC
Permalink
Peter Dimov schrieb:
[snip]
Post by Peter Dimov
I missed that thread... no 'boost', 'shared_ptr' or 'checked_delete' in the
subject. :-)
Anyway, making boost::checked_delete a friend - even if you could do it
;-) - is not guaranteed to work. boost::shared_ptr isn't required to use
boost::checked_delete.
Yes, you are right....I'm still learning ;)

Thank you all

Regards, Stephan
--
---------------------------------------------------------------
Dipl.-Inf. (FH) Stephan Born | beusen Solutions GmbH
fon: +49 30 549932-0 | Landsberger Allee 366
fax: +49 30 549932-21 | 12681 Berlin
mailto:***@beusen.de | Germany
---------------------------------------------------------------
PGP-Key verfügbar | PGP-Key available
---------------------------------------------------------------
Loading...