[smart_ptr] scoped_array / shared_array (size_t) constructor

Hi, Currently, one has to manually allocate the array and pass the ptr to the constructor of scoped_array / shared_array. boost::scoped_array<unsigned char> A(new unsigned char[100]); boost::shared_array<unsigned char> B(new unsigned char[100]); This requires one to repeat the type and shared_array requires two allocations (one extra for the control block). std::vector has a constructor from size_t and one can write: std::vector<unsigned char> V(100); This avoids the type duplication and it'd allow shared_array to do only a single allocation. A few choices have to be made: 1. Should "new T[sz]" or "new T[sz]()" be used? 2. What should happen when size = 0? Should it call new T[0] or should it just use NULL? https://svn.boost.org/trac/boost/ticket/4637 -- Olaf

On Wed, Oct 26, 2011 at 11:02 PM, Ivan Sorokin <vanyacpp@gmail.com> wrote:
Maybe off-topic, but why should one prefer scoped_array to vector? Just curious, I never used it myself.
Me neither, but scoped_array can take ownership of an existing array. And it can be used when construction of elements (like int) is unwanted. I expected it to also support release(), but it seems it doesn't. -- Olaf

On 10/26/2011 05:14 PM, Olaf van der Spek wrote:
The extra features of std::vector (appending, inserting, removing elements; iterators; size; etc.) make it nothing like scoped_array. -- Dick Hadsell 203-992-6320 Fax: 203-992-6001 Reply-to: hadsell@blueskystudios.com Blue Sky Studioshttp://www.blueskystudios.com 1 American Lane, Greenwich, CT 06831-2560 Follow Blue Sky Studios on Facebook <http://www.facebook.com/BlueSkyStudios> and Twitter <http://twitter.com/#%21/blueskystudios>

On 27.10.2011 01:14, Olaf van der Spek wrote:
Then "new T[sz]" will be better than "new T[sz]()".
2. What should happen when size = 0? Should it call new T[0] or should it just use NULL?
Will two overloads scoped_ptr(T*) and scoped_ptr(size_t) conflict with each other, when somebody calls scoped_ptr<T>(0)?

Ivan Sorokin wrote:
Will two overloads scoped_ptr(T*) and scoped_ptr(size_t) conflict with each other, when somebody calls scoped_ptr<T>(0)?
This is an interesting observation. It didn't occur to me that the size_t constructor can break existing code.

On Monday, October 31, 2011 21:20:18 Olaf van der Spek wrote:
I'm using it, from time to time. Frankly, I'm not sure there's much value in the suggested improvement. Smart pointers are not containers, so there's no need to follow the interface, especially considering ambiguities like boost::scoped_array<unsigned char> A(0); (is 0 a null pointer or a zero size of the array here?). Zero sized arrays are quite valid when dynamically allocated (the allocation result is not NULL in this case), so you can't always initialize the pointer to NULL in this case. FWIW, I would rather prefer make_scoped_array and make_shared_array function templates for this purpose, much like make_shared we already have. Of course, scoped_array would have to support move semantics in order to do that. In conjunction with auto keyword, one would not have to duplicate the array element type.

On Monday, October 31, 2011 22:26:16 Olaf van der Spek wrote:
It does, if it must not be NULL or NULL is a reserved value. In any case, the smart pointer should not alter semantics of the basic language constructs. This would be most counter-intuitive, IMHO.

On 10/31/2011 05:26 PM, Olaf van der Spek wrote:
If you want to implement this feature, you would have to advertise this as a change in the interface, not just a new feature, because, as people have pointed out, 0 would map to the size constructor rather than the default pointer. -- Dick Hadsell 203-992-6320 Fax: 203-992-6001 Reply-to: hadsell@blueskystudios.com Blue Sky Studioshttp://www.blueskystudios.com 1 American Lane, Greenwich, CT 06831-2560 Follow Blue Sky Studios on Facebook <http://www.facebook.com/BlueSkyStudios> and Twitter <http://twitter.com/#%21/blueskystudios>

On Mon, Oct 31, 2011 at 11:52 PM, Richard Hadsell <hadsell@blueskystudios.com> wrote:
Don't you keep the size somewhere else? The array seems kinda useless when you don't know the size.
Yeah, that's a problem. Olaf

On 10/31/2011 07:19 PM, Olaf van der Spek wrote:
If I replaced a pointer to an array with a std::vector, the code would be entirely different, and it would take up more memory. If I had tens of thousands of these pointers (which I do), I would definitely stick with the current scoped_array and adamantly protest any suggestion that would increase the memory footprint (as I once did a while ago). -- Dick Hadsell 203-992-6320 Fax: 203-992-6001 Reply-to: hadsell@blueskystudios.com Blue Sky Studioshttp://www.blueskystudios.com 1 American Lane, Greenwich, CT 06831-2560 Follow Blue Sky Studios on Facebook <http://www.facebook.com/BlueSkyStudios> and Twitter <http://twitter.com/#%21/blueskystudios>

On Tue, Nov 1, 2011 at 4:29 PM, Richard Hadsell <hadsell@blueskystudios.com> wrote:
Actually, what is the problem? Do you use (NULL) or (0) instead of just () (for construction).
This proposal does not change the footprint, does it? -- Olaf

On 11/01/2011 12:00 PM, Olaf van der Spek wrote:
Actually, what is the problem? Do you use (NULL) or (0) instead of just () (for construction).
I would have to check through a lot of code, not just my own, to look for places that are initialized with 0. I would expect to see this in initializers for data members that were formerly simple pointers.
This proposal does not change the footprint, does it? Not that I know of. I just wanted the participants in the discussion to be aware of this consideration.
-- Dick Hadsell 203-992-6320 Fax: 203-992-6001 Reply-to: hadsell@blueskystudios.com Blue Sky Studioshttp://www.blueskystudios.com 1 American Lane, Greenwich, CT 06831-2560 Follow Blue Sky Studios on Facebook <http://www.facebook.com/BlueSkyStudios> and Twitter <http://twitter.com/#%21/blueskystudios>

On Tue, Nov 1, 2011 at 5:13 PM, Richard Hadsell <hadsell@blueskystudios.com> wrote:
Adding a (size_t) constructor would produce ambiguity errors, but those are easy to fix by using the default constructor instead. Would that be acceptable? Olaf

On 11/01/2011 12:20 PM, Olaf van der Spek wrote:
-- Dick Hadsell 203-992-6320 Fax: 203-992-6001 Reply-to: hadsell@blueskystudios.com Blue Sky Studioshttp://www.blueskystudios.com 1 American Lane, Greenwich, CT 06831-2560 Follow Blue Sky Studios on Facebook <http://www.facebook.com/BlueSkyStudios> and Twitter <http://twitter.com/#%21/blueskystudios>

On Friday, November 04, 2011 16:51:12 Olaf van der Spek wrote:
I still don't like the idea. If you really want to add this feature, please retain the current interface intact. You could add a special manipulator to make the allpcation request explicit, like this: boost::scoped_array< int > p(boost::allocate_count(10)); Here allocate_count is a simple class that holds the size of the array to be allocated. Alternatively, Boost.Parameter could be used, but it is quite heavy for such basic tools like smart pointers.

On Fri, Nov 4, 2011 at 5:02 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
Why not?
retain the current interface intact. You could add a special manipulator to make the allpcation request explicit, like this:
What *exactly* is the interface you'd like to retain?
-- Olaf

I'll chime in. None of boost::{shared,scoped}_{ptr,array}<T> allocate T instances within their constructors. That's (part of) what make_shared is intended to do. Avoiding redundant type specifications is auto/BOOST_AUTO's job. Rather than adding potentially ambiguous constructor overloads to well-defined, heavily-used classes, spend the time implementing boost::make_{shared,scoped}_array instead. auto + make_{shared,scoped}_array will accomplish what you want (avoiding specifying the type twice) and the functionality will benefit a much broader audience. - Rhys

On Fri, Nov 4, 2011 at 6:02 PM, Rhys Ulerich <rhys.ulerich@gmail.com> wrote:
Why was it done that way (for shared_ptr) and does that rationale also apply to scoped/shared_array?
Do you mean the () vs (0) issue? Why's that such a big problem?
I'm confused. Why would it benefit a broader audience? auto and move constructors aren't available in C++03 and auto doesn't work for member vars. -- Olaf

On Friday, November 04, 2011 17:35:11 Olaf van der Spek wrote:
I've already expressed my opinion in my first post in this thread. It breaks interface without adding any significant value. It makes the interface ambiguous.
I want the smart pointer to be initializable with pointers. This includes explicit NULL.

On Fri, Nov 4, 2011 at 6:14 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
I want the smart pointer to be initializable with pointers. This includes explicit NULL.
Why? What's the benefit over using the default constructor? -- Olaf

On Saturday, November 05, 2011 01:18:53 Olaf van der Spek wrote:
I want this for interface consistency which can be useful in generic code and, well, is the expected behavior. Every smart pointer tries to mimic raw pointers and the constructor ambiguity reduces this similarity. I also don't want to update my code when it breaks.

On Sat, Nov 5, 2011 at 9:00 AM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
Why? What's the benefit over using the default constructor?
I want this for interface consistency which can be useful in generic code and,
Do you know existing generic code that'd be affected and couldn't use the default constructor?
What's the problem with updating your code? Also, could you address how you'd implement this in C++03? -- Olaf

On Saturday, November 05, 2011 12:08:48 Olaf van der Spek wrote:
I probably know the code that will be affected. Although I can't remember if it can be converted to default constructor (I don't have the code at hand, but it's probably possible). Either way, that doesn't change my mind. Allocating things is not smart pointers' business and I'd like to keep it that way.
I don't want to waste my time. I'm sure there will be others who aren't aware of this conversation and will be puzzled to discover that boost smart pointers can't be constructed from NULL. Honestly, can you give me a single reason why e.g. scoped_array can't be initialized with NULL? And why are you so eager to break the interface?
Also, could you address how you'd implement this in C++03?
What exactly? Moveability and make_scoped_array? Sure, take a look at boost::thread, it supports moving in C++03. With shared_array movability is even not required. Auto? BOOST_AUTO has already been mentioned. Yes, it won't help members. That's ok with me since I don't remember a single time when I had to use scoped_array as a member. Shared_array is more probable as a member, but I'm ok with it either. And my previous suggesion about named arguments to the constructor is also 100% doable in C++03. I would still prefer make_* functions though.

On Sat, Nov 5, 2011 at 12:38 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
What principle/rule says it should be done out-of-class? STL containers like string and vector do their own allocation.
I've never used (NULL) with a smart ptr. Why would you do that? Would be nice to see some real code that does that.
Honestly, can you give me a single reason why e.g. scoped_array can't be initialized with NULL? And why are you so eager to break the interface?
Code simplicity // typedef boost::shared_array<unsigned char> shared_data; 1. shared_data d0(256); 2. BOOST_AUTO(d1, make_shared_data(256)); 3. auto d2 = make_shared_data(256); 2 is much more verbose than 1, 3 is also more verbose than 1 I'd love to not break (NULL) and (0), but I'd also love to have simple code.
Thanks, I'll take a look.
The named parameter is possible but unfortunately also more complex. -- Olaf

Tradition coming from the smart pointer API. It seems fitting that smart pointer API should continue following its own traditions.
All three are simple, readable, and straightforward. 1 looks like a container (but it is not a container). 2 and 3 look like what I'd expect given the existing smart pointer APIs. Re: verbosity. We've all collectively spent more keystrokes responding to this thread than one will likely save in code abbreviated by the proposed feature. Olaf, beyond saving some keystrokes, what genuinely new capability does this proposed feature add? - Rhys

On Sat, Nov 5, 2011 at 5:45 PM, Rhys Ulerich <rhys.ulerich@gmail.com> wrote:
I think I already asked you, but: Why does shared_ptr / make_shared do it that way and does that reason apply to shared_array?
Don't you consider 1 more simple and readable than 2? Does something like Visual Studio Intellisense work with 2?
container (but it is not a container). 2 and 3 look like what I'd
Isn't it kinda like a shared container (except it doesn't know it's size etc)?
expect given the existing smart pointer APIs.
Readability is more important than writability. I'd also like to see a shared_array that knows it's size, this is a first step towards that. Olaf

On Saturday, November 05, 2011 18:19:26 Olaf van der Spek wrote:
I don't know the original reasons but in my understanding external allocation is more flexible. One can control where and how the pointed object is allocated and constructed. In case of shared_ptr, one can also specify the deleter at the allocation place. All this simplifies implementation of different patterns, such as factory or singleton. Since array pointers are also pointers, it is straightforward for them to follow the interface guideline started by shared_ptr.
I'd like to repeat that pointers are not containers. Their purpose and application are quite different, and hence there's no reason to follow containers interface. If you want things like size, iterators and built-in memory management, just use containers. And if you want a shared container, just use shared_ptr with a container.

On Sat, Nov 5, 2011 at 6:53 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
Might be a good idea to get to know the reasons before you copy the design.
It's not a xor. Supporting a (size_t) constructor does not mean you can't (also) implent make_ and allocate_ functions.
It's about shared_array. Arrays are pointers are not synonyms.
Stop kidding me please. shared_ptr<vector> is totally unsuitable. -- Olaf

On Sunday, November 06, 2011 12:15:35 Olaf van der Spek wrote:
Why would we need the ambiguous constructor then? Just do "the right thing" from the start.
Actually, they are. The only difference is the deallocation method. They stop being similar when you add advanced features I mentioned, then arrays become containers.
Why?

On Sun, Nov 6, 2011 at 2:19 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
Because some people prefer the simpler syntax.
shared_ptr doesn't provide operator[], shared_array does. So your statement is clearly false. char a[128] char* p = a; Is sizeof(a) == sizeof(p)? Array and pointer were synonyms, right?
For example, I can't put a memory mapped file into a vector. I don't (always) want the memory to be initialized. I don't want two allocations. I do want sub arrays. -- Olaf

On Sunday, November 06, 2011 22:06:41 Olaf van der Spek wrote:
A few keystrokes don't justify the change, IMO.
Come on. We were talking of dynamic arrays, weren't we? I feel this is going nowhere. You clearly prefer to sacrifice interface clarity to less typing. Sorry, I don't share your point. Seeing other comments, it is clear to me that many other people are also against the change. If you're feeling strong about this, my suggestion is to create a separate component and leave the current smart pointers as they are. I'm sure this new component will find its audience.

On Mon, Nov 7, 2011 at 12:20 AM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
That's not what you said. You also 'conviniently' skipped the operator[] argument.
Many? I haven't seen a single one that could show real world code that'd be affected by the ambiguity. Haven't seen more arguments than 'let's just copy make_shared' either.
change. If you're feeling strong about this, my suggestion is to create a separate component and leave the current smart pointers as they are. I'm sure
Might be a good idea. -- Olaf

On Saturday, November 05, 2011 17:16:30 Olaf van der Spek wrote:
Sometimes less keystrokes does not produce a cleaner code. I mean, in the (1) case I would expect shared_data to be a container or some custom class. Last thing I would expect it to be is a pointer.

On 05/11/11 16:16, Olaf van der Spek wrote: <snip>
I've never used (NULL) with a smart ptr. Why would you do that? Would be nice to see some real code that does that.
I was curious, I searched for this usage on Google code search <http://www.google.com/codesearch#/>. I found one example: <http://aanf.googlecode.com/svn/trunk/aanf/asio-based/include/connection_pool.hpp> Clearly this could be changed to the default constructor, but it evidently does happen that people pass 0. I can't quite figure out Google code search's interface, but I think this is the only meaningful hit for any of my searches. John Bytheway

On Sun, Nov 6, 2011 at 11:59 AM, John Bytheway <jbytheway+boost@gmail.com> wrote:
This one boost::shared_ptr(0);? That's shared_ptr, not shared_array. Anyway, it looks like a very limited amount of code would be broken. -- Olaf

On Sat, Nov 5, 2011 at 1:09 PM, Peter Dimov <pdimov@pdimov.com> wrote:
Are you using explicit construction with NULL or 0? If so, maybe you could tell us why you're doing that instead of using the default constructor? -- Olaf

Olaf van der Spek wrote:
Does anyone else have comments / concerns about a (size_t) constructor?
Yes. First, please go to the standards committee and get them to add this to raw pointers: float* ptr = new float[128]; // too much typing! float* ptr(128); // much better! When they have agreed to that, come back here and suggest that scoped_array<> and shared_array<> are updated to match. As I see it, scoped_array<> is the THIN wrapper around a raw pointer that delete[]s it when it goes out of scope. And nothing more. Regards, Phil.

Hi all! I found "logical error" in Boost.DateTime. Example: int main() { using namespace boost::posix_time; ptime tm( time_from_string( "2011-01-01 00:00:00" ) ); std::cout << tm << std::endl; } Output is ok: 2011-Jan-01 00:00:00 But if we don't specify the time: int main() { using namespace boost::posix_time; ptime tm( time_from_string( "2011-01-01" ) ); std::cout << tm << std::endl; } Output (on my computer) is strange: 2011-Mar-25 19:01:01 There is no any exception, but just... garbage. Moreover: int main() { using namespace boost::posix_time; ptime tm( from_iso_string( "20110101T000000" ) ); std::cout << tm << std::endl; } Output is ok: 2011-Jan-01 00:00:00 But if we don't specify the time: int main() { using namespace boost::posix_time; ptime tm( from_iso_string( "20110101" ) ); std::cout << tm << std::endl; } Output is also strange: 2011-Jan-01 20:11:01.100000 There is no any exception too, but just garbage. I think it is "logical error". If we define just date (i.e. particular day of particular month of particular year) but don't specify the time explicitly, it's logical to assume we had in mind the beginning of this day, i.e. 00:00:00. So I think in functions time_from_string() and from_iso_string() must exists checking of separator's existence (space or 'T'), and if it's not - time must be 00:00:00. Yes, we can create correct ptime object with "zero" time using gregorian::date, for example: int main() { using namespace boost::posix_time; ptime tm( boost::gregorian::date( 2011, 1, 1 ) ); std::cout << tm << std::endl; } In this case we have correct "zero" time: 2011-Jan-01 00:00:00 But it's important that if functions time_from_string() and from_iso_string() _DO_NOT_ALLOW_ define date without explicit time - we must get exception, but not a random garbage. - Denis
participants (10)
-
Andrey Semashev
-
Ivan Sorokin
-
John Bytheway
-
Olaf van der Spek
-
Peter Dimov
-
Phil Endecott
-
Rhys Ulerich
-
Richard Hadsell
-
Vicente J. Botet Escriba
-
Шевченко Денис