Boost logo

Boost :

Subject: Re: [boost] [shared_array] Why not in C++11 ?
From: Sid Sacek (ssacek_at_[hidden])
Date: 2013-07-08 03:59:07

Glen Fernandes wrote:
> I can see why you desire this. One concern is that boost::shared_ptr<T[]> is
> usable without boost::make_shared. With boost::make_shared, yes, in my
> implementation the size is stored somewhere and changing the machinery could
> possibly surface that through boost::shared_ptr's interface. But what happens
> when boost::shared_ptr<T[]> is used with operator new[]? The size is not stored
> in that case.
> You mentioned in an earlier mail that you would prefer that size() return a value
> like -1 in such a case. That also feels very strange to me; i.e. returning a size_t
> but reserving a value like -1 to indicate unknown size.
> Glen

My original idea to return -1 was not very good. Zero is much better.

I'm new to 'shared_ptr< T[] >'; I don't know how long it's been out there and being
used in production code, but if all the rules for shared_ptr< T[] > have not yet
been set in stone, then maybe there's room for modifications.

My original thought was to make 'size' a requirement for shared_ptr< T[] > since why
would anyone use an array without knowing the array's bounds ? Every array has bounds
and nobody would touch an array without knowing that information. And placing that
information with the array itself makes the most sense to me.

If it were made a requirement, then the code might look something like this, or
something even more clever:

                shared_ptr< char[] > buffer( new char[ 10 ], 10 );

The above constructor would set the capacity value to 10. If however, the coder were
to simply do this:

                shared_ptr< char[] > buffer( new char[ 10 ] );

It is clear that the coder has no interest in the capacity information for the buffer,
and therefore no other part of that programmer's code will be looking for it either.
This would be a non-breaking change, since that is the default behavior of shared_ptr<T[]>,
and the capacity could simply be set to 0.

Let me describe how this feature could be used. Imagine your socket library filled user
buffers as data arrived. For example:

        socket::fill_buffer( shared_ptr<char[]> buffer )

        my_socket-> fill_buffer( my_buffer );

In this case, your library doesn't simply want a shared buffer pointer, but it also needs
to know where the buffer ends. Of course you could specify that with extra arguments, which
is how it has traditionally been done in C, but with buffer objects, the buffer pointer and
its size will be kept atomic. That could prevent buffer overflow bugs from occurring.

If a piece of code were to then call 'fill_buffer' with an improperly initialized buffer
object, the library would see a zero value for the capacity and throw an exception, or simply
return from the call without doing anything. It is the contract of the library's method to
be provided with a correctly initialized buffer object. This next piece of code would fail:

        shared_ptr< char[] > my_buffer( new char[ 10 ] );
        my_socket-> fill_buffer( my_buffer ); // OOPS !! improperly initialized buffer !!!

When you write this code:

        shared_ptr< char[] > buffer( new char[ 10 ] );

The buffer object no longer behaves like a regular shared_ptr<>, but behaves very much like
an array, and for all intents and purposes, it is an array, it is a buffer! What I mean is,
it is not a pointer anymore, and you can no longer do this:

        buffer-> ???? // what can the pointer operator be used for ????

So, in order to complete these kinds of shared-buffer (shared-array) objects, it only makes
sense to tie the capacity together with the buffer pointer. That's my humble opinion.

-Sid Sacek

Boost list run by bdawes at, gregod at, cpdaniel at, john at