Boost logo

Boost :

From: Jason Hise (chaos_at_[hidden])
Date: 2005-04-28 16:59:41


Rob Stewart wrote:

>From: Jason Hise <chaos_at_[hidden]>
>
>
>>There are explicit create and destroy member functions, and the
>>singleton is generally scheduled to be automatically destroyed at one
>>point or another. As a result, the automatic creation when operator ->
>>is invoked is generally a last line of defense, which will recreate the
>>instance if it has been destroyed (or in some rare circumstances will
>>create the instance for the first time).
>>
>>So long as client code makes sure that the instance has already been
>>created, that nothing destroys the instance while the raw pointer is in
>>use, and that operations which need to be locked are locked manually,
>>then that raw pointer is safe to use.
>>
>>
>
>Do the same problems exist when using the singleton object within
>the context of operator ->()?
>
>
Only if the member function being called destroys the instance, which is
the moral equivalent of "delete this;"

>>>>>Having said that, is it really the case that operator *()
>>>>>couldn't do the same work as operator ->() (possibly adding an
>>>>>exception if you can't get the underlying object)?
>>>>>
>>>>>
>>>>>
>>>>The difference is that the pointer returned by operator -> is guaranteed
>>>>to be used immediately, and by using an intermediate pointer class I can
>>>>construct a lock for the duration of the member call (this technique is
>>>>described in Modern C++ Design, in the smart pointer section).
>>>>
>>>>
>
>Note that I can write this:
>
> class A { ... };
> // whatever the proxy's name is:
> typedef boost::singleton<A>::some_proxy proxy_type;
> proxy_type proxy(s.operator ->());
> proxy->whatever();
>
>
Actually, the proxy type (which is called access_pointer in the code) is
a private member class of the pointer class, and thus variables of that
type cannot be created or talked about by client code.

>>>>Operator * is incapable of constructing this type of temporary lock, and
>>>>additionally there is no guarantee that the reference returned would
>>>>used and discarded immediately. Once the actual reference is returned,
>>>>no more checking can take place.
>>>>
>>>>
>>>Why can't you return such a proxy from a member function like
>>>"get" or "get_object" and let that proxy convert to the singleton
>>>type?
>>>
>>>
>
>That means I can write
>
> A & object(*proxy);
>
>or
>
> A & object(proxy.get_object());
>
>and then I can do what I like with object.
>
>
Ahh, I think I understand now. So essentially, pointer::operator * ()
would return a 'smart reference' class instance?

>>>That means you can spell "get_unsafe_ptr" without "unsafe"
>>>because the proxy ensures that it is safe (as safe as with
>>>operator ->(), anyway).
>>>
>>>
>>>
>>The action of getting the pointer itself is not unsafe. What is unsafe
>>is maintaining that pointer and storing it for an extended period of
>>time, during which the singleton could be destroyed and recreated.
>>
>>
>
>Right. As I see it, you've taken steps to ensure that the
>singleton survives so long as the proxy returned from
>operator ->() lives.
>
Sort of. I ensure that the singleton is created immediately before a
member is accessed, and use a lock to ensure that no other thread
destroys or uses the singleton while the operation is in progress.
However, the proxy does not directly disable the destroy member function
upon creation and re-enable it upon destruction. This is generally
unnecessary because calls by other threads to destroy will be locked,
and the member function shouldn't be making a call to try to destroy itself.

>Similarly, operator *() can return the same
>(or a similar) proxy thus keeping the object alive during its
>use. If that proxy provides a conversion operator (gasp!) to the
>object managed by the singleton smart pointer, then you get
>complete safety and access to the raw object.
>
>The conversion operator (probably only appropriate on a second
>proxy type) make this possible:
>
> A & object(proxy);
>
>
This is clever and would probably work. However, I have two questions:

A) What benefits would a 'smart reference' provide that the smart
pointer doesn't already?

B) Would such benefits outweigh the fact that client code would now be
capable of accidentally storing and using a real reference to the
singleton? For instance:

class MySingleton : public basic_singleton < MySingleton >
{
};

// client code
class Foo
{
private:
    MySingleton & instance;

public:
    Foo ( MySingleton::pointer ptr ) : instance ( *ptr )
    {
    }
};

>>In almost every circumstance, it is preferable for client code to use
>>and pass around singleton::pointers rather than singleton *s, just like
>>it is preferable for client code to use smart pointers to manage memory
>>rather than trying to manage memory and raw pointers manually. Managing
>>them manually can be done safely, but it is harder and error prone.
>>
>>
>
>I understand the motivation for the original question. I'm just
>having a hard time getting you to see my point, it would seem.
>Hopefully, spelling out more of the details has helped.
>
I think I understand your idea now, please correct me if I am mistaken.

-Jason


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk