Why no non-null smart pointers?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
17 messages Options
Reply | Threaded
Open this post in threaded view
|

Why no non-null smart pointers?

Ross MacGregor
I just created a very small smart pointer libary for our project. It will
contain two types of shared pointers: SharedHandle and NullableSharedHandle.
SharedHandle will be a shared pointer that cannot be null by ensuring a
non-null construction while NullableSharedHandle will basically be a typedef
for boost::shared_ptr.

I firmly believe that code can be made more reliable by minimizing the use
of nullable pointers. To this end, I think it is important that we start
using non-null pointer types. Ideally the compiler would be able to
understand a non-null pointer and catch bugs at compile time, but I think we
can still improve things with a few runtime checks. If we make sure that a
shared pointer is constructed with a non-null pointer and ban any reset()
type methods, we guarentee that it points to something. This can be
beneficial for the following reasons:

1. Any function that uses this type will no longer be burdened with
precondition checks to make sure variables of this type are not null.
2. It's type is simply more explicit and better explains its function.
3. Moving the non-null check to the creation phase will reduce the
possibility of encountering null pointer exceptions at runtime (assuming
there are many more pointer references than constructor calls).

So my little pointer library seems to make a lot of sense, but if so why
hasn't this simple smart pointer variation made it into boost yet? Has this
been discussed somewhere already? My search on non-null smart pointers is
not revealing much discussion on the topic.

Regards,
Ross
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Johan Råde
Ross MacGregor wrote:
> I just created a very small smart pointer libary for our project. It will
> contain two types of shared pointers: SharedHandle and NullableSharedHandle.
> SharedHandle will be a shared pointer that cannot be null by ensuring a
> non-null construction while NullableSharedHandle will basically be a typedef
> for boost::shared_ptr.
>
> I firmly believe that code can be made more reliable by minimizing the use
> of nullable pointers.

[snip]

Your idea certainly could be useful.
It should probably be combined with the Null Object Pattern.

However, having many different smart pointers would make it more difficult
for different software components to interact with each other.

--Johan Råde

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Frank Mori Hess
In reply to this post by Ross MacGregor
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Wednesday 02 April 2008 12:58 pm, Ross MacGregor wrote:
> I just created a very small smart pointer libary for our project. It will
> contain two types of shared pointers: SharedHandle and
> NullableSharedHandle. SharedHandle will be a shared pointer that cannot be
> null by ensuring a non-null construction while NullableSharedHandle will
> basically be a typedef for boost::shared_ptr.

If you're going to make a shared_ptr wrapper that acts like a reference (no
null, no reset, I assume no assignment) then why not complete the set and add
another that acts like a value?  That is, one that does deep copies of the
template type on assignment, and it could also have the non-null requirement.  
It would differ from an ordinary value in that you could obtain shared_ptr
(or your other wrapper types) from it which share ownership.  It would be
useful for cases where you currently have a class with a shared_ptr member
but need to do deep copies of the object owned by the shared_ptr in the
containing class' assignment/copy constructor.

- --
Frank
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQFH9NmK5vihyNWuA4URAg76AJ4/j4DbkRbaiS4KsByXXN+VAoh8uQCeNjY4
KMf3tP3jIKffiOFMZzRLMpA=
=IIlH
-----END PGP SIGNATURE-----
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Ross MacGregor
In reply to this post by Johan Råde
"Johan Råde" <[hidden email]> wrote in message
news:ft10ag$6nn$[hidden email]...

>Your idea certainly could be useful.
>It should probably be combined with the Null Object Pattern.

Yes, it would be a way to enforce the Null Object Pattern.
Although one should only use null objects where they make sense.
Null objects should not be used to pass additional state
information. If you create a find function that returns a null
object if no items are found, you really gain nothing from using
the pattern and the code will probably be more prone to error.

>However, having many different smart pointers would make it more
>difficult for different software components to interact with
>each
>other.

Yes I think this variant of smart pointer will encounter
resistance from the community because it is much easier to have
only one shared_ptr to worry about. This is why we have the
boost::shared_ptr and not a policy based one. The nullability
attribute would fit nicely into a policy based smart pointer as
the policy design avoids the complexity explosion of many
separate classes.

Thinking about this some more, I think there may be a more
fundamental problem at play here. I would suggest that smart
pointers as we have come to know them are poorly designed. It's
only natural that they evolved to reflect the capabilities of the
C++ pointer (indeed they are called smart POINTERS), but are the
semantics of C++ pointers a good idea in terms of a class
interface? I would argue that they are not. A smart pointer fails
the One Responsibility Rule regarding the its nullability.
Allowing a smart pointer to be null is an extra state carried by
the class when it could be maintained by other means.

If we remove null capability from all smart pointers, essentially
turning them into smart references, what do we do when we
absolutely need to represent a reference that does not exist (or
dare I say optional)? Well boost already has the answer. We use
boost::optional; a template for representing optional values.
Using boost::optional would free us from a proliferation of smart
pointers (nullable and not) and usability concerns.

I'm sure no one is going argue that optional<int> creates too
much interface friction because int is used commonly as component
interface type. Therefore optional<WidgetRef> should not create
problems for components that use WidgetRef.

I know it is an insane idea to suggest changing the entire
boost::smart_ptr library to a boost::smart_ref libary, but I
think it may actually be the right thing to do. Does anyone
agree?

-Ross




_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Jeff Hill
Ross MacGregor wrote
"Johan Råde" <[hidden email]> wrote in message
news:ft10ag$6nn$[hidden email]...

I know it is an insane idea to suggest changing the entire
boost::smart_ptr library to a boost::smart_ref library, but I
think it may actually be the right thing to do. Does anyone
agree?
I also find the nill state of shared_ptr to be a liability when designing interfaces.  The primary purpose of shared_ptr when used in an interface context is to convey ownership, and almost always it feels wrong to also provide simultaneously an option to convey ownership of nothing. The poor user is left with the responsibility of testing every shared_ptr he has received for null state before using it, but its easy to forget and the compiler provides no assistance. Ultimately, the library interface based on shared_ptr is more error prone, less likely to be easy to use, and therefore arguably suboptimal.

Of course no one suggests that shared_ptr should (or could) be changed in ways that impacts backwards compatibility, but I have to agree with the author of the post that this issue can be identified as a legitimate cause for concern.

Jeff

Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Jeff Hill
Perhaps there is even a backwards compatible solution. Could we employ an
optional null object pattern in methods and or constructors of shared_ptr
that might establish a raw nill state? Perhaps something like this obviously
oversimplified example could be use.

template < typename T>
void shared_ptr < T > :: reset ( T * p = 0 )
{
    if ( p ) {
          /* what happens in shared_ptr now */
    }
    else {
        /* new behavior in shared_ptr, when it detects a nill raw pointer */
        static NullObjectTypes < T > nullObject;
        *this = shared_ptr < T > ( &nullObject, NullObjectTypes < T > ::
deleter () );
    }
}

Jeff



--
View this message in context: http://boost.2283326.n4.nabble.com/Why-no-non-null-smart-pointers-tp2642959p4661799.html
Sent from the Boost - Dev mailing list archive at Nabble.com.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Jeff Hill
This post was updated on .
In reply to this post by Jeff Hill
CONTENTS DELETED
The author has deleted this message.
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Rob Stewart-6
In reply to this post by Jeff Hill
On April 28, 2014 11:20:35 AM EDT, Jeff Hill <[hidden email]> wrote:

>Ross MacGregor wrote
>> "Johan Råde"
>>
>> I know it is an insane idea to suggest changing the entire
>> boost::smart_ptr library to a boost::smart_ref library, but I
>> think it may actually be the right thing to do. Does anyone
>> agree?
>
>I also find the nill state of shared_ptr to be a liability when
>designing
>interfaces.  The primary purpose of shared_ptr when used in an
>interface
>context is to convey ownership, and almost always it feels wrong to
>also
>provide simultaneously an option to convey ownership of nothing. The
>poor
>user is left with the responsibility of testing every shared_ptr he has
>received for null state before using it, but its easy to forget and the
>compiler provides no assistance.

You've overlooked the use case of reclaiming ownership from smart_ptr. There's nothing left for it to own after that. (You can't replace it with a default constructed instance, as there may be no default constructor. Any other value you choose would be a sentinel value and you'd want to detect that in many contexts, so checking for null is more straightforward.)

OTOH, you could justify a new class that has no reset() and that rejects null pointers, so it is never null, yet has shared ownership semantics.

___
Rob

(Sent from my portable computation engine)

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Andrey Semashev-2
In reply to this post by Jeff Hill
On Mon, Apr 28, 2014 at 9:42 PM, Jeff Hill <[hidden email]> wrote:

> Maybe there /is/ even a backwards compatible solution.
>
> Perhaps an optional null object pattern could be employed in methods, and or
> constructors, of shared_ptr that might establish a raw nill state? Perhaps
> something like this obviously oversimplified example could be use.
>
> template < typename T>
> void shared_ptr < T > :: reset ( T * p = 0 )
> {
>     if ( p ) {
>           /* what happens in shared_ptr now */
>     }
>     else {
>         /* new behavior in shared_ptr, when it detects a nill raw pointer */
>         *this = NullObjectTypes < T > :: factory ();
>     }
> }

Please, don't change shared_ptr (or any other *_ptr) behavior. They
are intended to behave as pointers, and pointers do have a null state.
This is a useful feature in many cases.

I'd prefer the functionality you're looking for to be implemented as a
separate component/library. It may be based on pointers internally
though.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Jeff Hill
In reply to this post by Rob Stewart-6
> You've overlooked the use case of reclaiming ownership from smart_ptr.

Sorry, apparently a bit more explanation is needed.

In the code above I have this assignment.

 *this = NullObjectTypes < T > :: factory ();

My idea is that shared_ptr <T> is returned from this factory function so that it can be assigned to *this, and so ownership is properly maintained.

The default templated version of NullObjectTypes < T > :: factory () would return a default constructed shared_ptr <T>. This is necessary of course for backwards compatibility.

template < class T >
struct NullObjectTypes
    shared_ptr < ChessPiece  >   factory () { return shared_ptr < T > (); }
};

The benefit comes when T is an interface class. In this situation we can specialize NullObjectTypes < T > as follows.

Lets say that T is the ChessPiece class, and we will implement the null object pattern for it.

struct ChessPiece {
    virtual void move() =  0;
};

struct ChessPieceNull : public ChessPiece {
    void move() {}
};

template <>
NullObjectTypes < ChessPiece   >
    shared_ptr < ChessPiece  >   factory ();
};

template <>
shared_ptr < ChessPiece  > NullObjectTypes < ChessPiece   > ::  factory ()
{
    static ChessPieceNull chessPieceNull;
    return shared_ptr < ChessPiece > ( &chessPieceNull, nullDeleter () );
}
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Jeff Hill
So I deleted the wrong post. This is the version of the proposed change that I intended to keep.

template < typename T>
void shared_ptr < T > :: reset ( T * p = 0 )
{
    if ( p ) {
          /* what happens in shared_ptr now */
    }
    else {
        /* new behavior in shared_ptr, when it detects a nill raw pointer */
        *this = NullObjectTypes < T > :: factory ();
    }
}
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Jonathan Wakely-2
In reply to this post by Jeff Hill
On 29 April 2014 15:32, Jeff Hill wrote:
> template <>
> shared_ptr < ChessPiece  > NullObjectTypes < ChessPiece   > ::  factory ()
> {
>     static ChessPieceNull chessPieceNull;
>     return shared_ptr < ChessPiece > ( &chessPieceNull, nullDeleter () );
> }

N.B. This means every shared_ptr that owns the NullObject gets created
with unique ownership, creating a new control block on the heap.

Sometimes that might be what you want, but other times it would be
better to copy an existing object so all null objects of a given type
share ownership:

template <>
shared_ptr < ChessPiece  > NullObjectTypes < ChessPiece   > ::  factory ()
{
    static ChessPieceNull chessPieceNull;
    static shared_ptr < ChessPiece > nullsp( &chessPieceNull, nullDeleter () );
    return nullsp;
}

(This comment shouldn't be seen as endorsement of the proposal ;-)

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Jeff Hill
>    static ChessPieceNull chessPieceNull;
>    static shared_ptr < ChessPiece > nullsp( &chessPieceNull, nullDeleter () );
>    return nullsp;

Thanks for your suggestion. Agree this is usually preferable, but since the code is in the specialization (within a user snap-in) then of course the user can decide which approach he prefers.
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Lars Viklund
In reply to this post by Jeff Hill
On Tue, Apr 29, 2014 at 07:44:27AM -0700, Jeff Hill wrote:
> So I deleted the wrong post. This is the version of the proposed change that
> I intended to keep.

Note that mailing lists (which this actually is) doesn't _do_ deletion.
If you want to rescind a post, reply to it and inform people that the
parent should be unconsidered.

Any operation that your particular groups/news-alike gateway promises is
a "delete" is a completely local thing. No-one outside of your gateway
will ever see it, maybe not even outside of you.

--
Lars Viklund | [hidden email]

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Jeff Hill
In reply to this post by Jeff Hill
I was considering this while I was at the dentist, and  I can identify a flaw with my proposal. The "NullObjectTypes < T > :: factory ()" returns a default constructed shared_ptr by default, and if this default version is called from any of the constructor's that detect attempts to enter the shared pointer into a nill internal raw pointer state, then there will be infinite recursion.

Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Matt Calabrese
In reply to this post by Jeff Hill
On Mon, Apr 28, 2014 at 8:20 AM, Jeff Hill <[hidden email]> wrote:

> I also find the nill state of shared_ptr to be a liability when designing
> interfaces.  The primary purpose of shared_ptr when used in an interface
> context is to convey ownership, and almost always it feels wrong to also
> provide simultaneously an option to convey ownership of nothing. The poor
> user is left with the responsibility of testing every shared_ptr he has
> received for null state before using it, but its easy to forget and the
> compiler provides no assistance. Ultimately, the library interface based on
> shared_ptr is more error prone, less likely to be easy to use, and
> therefore
> arguably suboptimal.
>
> Of course no one suggests that shared_ptr should (or could) be changed in
> ways that impacts backwards compatibility, but I have to agree with the
> author of the post that this issue can be identified as a legitimate cause
> for concern.
>
> Jeff


There is usefullness to null and non-null smart pointers. Just so we're not
repeating thoughts here too much, this was also discussed at the end of
last year:
http://lists.boost.org/Archives/boost/2013/10/206732.php

I think the general consensus is that people want non-null smart pointers
(myself included), but there are many subtleties that people disagree on.
Be sure to read that thread going down paths that have been traveled before.

--
-Matt Calabrese

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Why no non-null smart pointers?

Jeff Hill
I did mention that, "no one suggests that shared_ptr should (or could) be changed in ways that impacts backwards compatibility."

What I am considering is if there is a way to make the shared_ptr implement the null object paradigm, but only for a particular user type T, but otherwise not impact current behavior of  shared_ptr including revealing in its public interface all that is good and bad with the nill state of raw pointers.

In the example above type ChessPiece might enforce the null object pattern for shared_ptr but user types that don't specialize the null object factory class would retain exactly current behavior.

Thanks for the reference, I will definitely have a close look.

Jeff