Smart Pointer new/Forwarding Problem Solved (by Force)

I've developed some facilities that perhaps ought to be developed into full-fledged libraries or components of existing Boost libraries. This work centers around my long-standing threat to build a library that could create smart pointers safely, without ever exposing a raw pointer to the user. For example, #include <memory> #include <boost/shared_ptr.hpp> #include "new.hpp" struct foo { foo(int&, char const*, std::auto_ptr<int> const&); }; std::auto_ptr<int> x(new_<int>(3)); std::auto_ptr<foo> y(new_<foo>(*x, "hello, world", x)); boost::shared_ptr<foo> z(new_<foo>(*x, (char const*)"hello, world", x)); To solve this problem correctly, it was necessary to address "the forwarding problem" (http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm). To that end, I developed some preprocessor macros that one can use to generate the necessary overload sets. Is there interest in adopting any of this code (enclosed), and if so, where should it go? -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
I've developed some facilities that perhaps ought to be developed into full-fledged libraries or components of existing Boost libraries. This work centers around my long-standing threat to build a library that could create smart pointers safely, without ever exposing a raw pointer to the user. For example,
#include <memory> #include <boost/shared_ptr.hpp> #include "new.hpp"
struct foo { foo(int&, char const*, std::auto_ptr<int> const&); };
std::auto_ptr<int> x(new_<int>(3)); std::auto_ptr<foo> y(new_<foo>(*x, "hello, world", x)); boost::shared_ptr<foo> z(new_<foo>(*x, (char const*)"hello, world", x));
To solve this problem correctly, it was necessary to address "the forwarding problem" (http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm ). To that end, I developed some preprocessor macros that one can use to generate the necessary overload sets. Is there interest in adopting any of this code (enclosed), and if so, where should it go?
Yes, I'm interested. IMHO maximum arity of about 5 isn't too restrictive. Except of this I would like to have an alternative new_ with greater arity which use only const reference forwarding. Non-const referrence parameters can be explicitly wrapped with boost::ref. I'm confused with syntax. new_<T> always creates auto_ptr<T>. How can I create for example shared_ptr<T>? How this is related to auto_overhead? Regards, Vaclav

On Wednesday 29 March 2006 08:05, Václav Veselý wrote:
I'm confused with syntax. new_<T> always creates auto_ptr<T>. How can I create for example shared_ptr<T>?
You probably can't, but: - std::auto_ptr is much less resource intensive (shared_ptr requires an additionally, dynamically-allocated structure to hold some internals) - you don't need to, as auto_ptr converts to shared_ptr, there is a special ctor taking an auto_ptr Uli

Ulrich Eckhardt <doomster@knuut.de> writes:
On Wednesday 29 March 2006 08:05, Václav Veselý wrote:
I'm confused with syntax. new_<T> always creates auto_ptr<T>. How can I create for example shared_ptr<T>?
You probably can't, but: - std::auto_ptr is much less resource intensive (shared_ptr requires an additionally, dynamically-allocated structure to hold some internals) - you don't need to, as auto_ptr converts to shared_ptr, there is a special ctor taking an auto_ptr
Unfortunately for this particular facility, the converting constructor is explicit, so you can't do f(new_<T>(a, b, c)) if f takes a shared_ptr<T>. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave@boost-consulting.com> writes:
Ulrich Eckhardt <doomster@knuut.de> writes:
On Wednesday 29 March 2006 08:05, Václav Veselý wrote:
I'm confused with syntax. new_<T> always creates auto_ptr<T>. How can I create for example shared_ptr<T>?
You probably can't, but: - std::auto_ptr is much less resource intensive (shared_ptr requires an additionally, dynamically-allocated structure to hold some internals) - you don't need to, as auto_ptr converts to shared_ptr, there is a special ctor taking an auto_ptr
Unfortunately for this particular facility, the converting constructor is explicit, so you can't do
f(new_<T>(a, b, c))
if f takes a shared_ptr<T>.
I just realized we could probably fix this by removing an "explicit" from the ctor that accepts an rvalue auto_ptr. What about that, Peter? This seems like a poster child for rvalue distinction! -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
David Abrahams <dave@boost-consulting.com> writes:
Unfortunately for this particular facility, the converting constructor is explicit, so you can't do
f(new_<T>(a, b, c))
if f takes a shared_ptr<T>.
I just realized we could probably fix this by removing an "explicit" from the ctor that accepts an rvalue auto_ptr. What about that, Peter? This seems like a poster child for rvalue distinction!
Done.

"Peter Dimov" <pdimov@mmltd.net> writes:
David Abrahams wrote:
David Abrahams <dave@boost-consulting.com> writes:
Unfortunately for this particular facility, the converting constructor is explicit, so you can't do
f(new_<T>(a, b, c))
if f takes a shared_ptr<T>.
I just realized we could probably fix this by removing an "explicit" from the ctor that accepts an rvalue auto_ptr. What about that, Peter? This seems like a poster child for rvalue distinction!
Done.
Cool! Just in case it wasn't obvious: we should do this across all the smart pointers. It looks like scoped_ptr could use the same treatment, for example. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
"Peter Dimov" <pdimov@mmltd.net> writes:
David Abrahams wrote:
David Abrahams <dave@boost-consulting.com> writes:
Unfortunately for this particular facility, the converting constructor is explicit, so you can't do
f(new_<T>(a, b, c))
if f takes a shared_ptr<T>.
I just realized we could probably fix this by removing an "explicit" from the ctor that accepts an rvalue auto_ptr. What about that, Peter? This seems like a poster child for rvalue distinction!
Done.
Cool! Just in case it wasn't obvious: we should do this across all the smart pointers. It looks like scoped_ptr could use the same treatment, for example.
scoped_ptr already takes its auto_ptr argument by value, so it doesn't need a separate rvalue overload. There should be no need to make the constructor implicit since a scoped_ptr cannot be used as a function argument because of its noncopyability.

"Peter Dimov" <pdimov@mmltd.net> writes:
David Abrahams wrote:
Just in case it wasn't obvious: we should do this across all the smart pointers. It looks like scoped_ptr could use the same treatment, for example.
scoped_ptr already takes its auto_ptr argument by value, so it doesn't need a separate rvalue overload. There should be no need to make the constructor implicit since a scoped_ptr cannot be used as a function argument because of its noncopyability.
Duh. Thanks for setting me straight. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
David Abrahams <dave@boost-consulting.com> writes:
Ulrich Eckhardt <doomster@knuut.de> writes:
On Wednesday 29 March 2006 08:05, Václav Veselý wrote:
I'm confused with syntax. new_<T> always creates auto_ptr<T>. How can I create for example shared_ptr<T>?
You probably can't, but: - std::auto_ptr is much less resource intensive (shared_ptr requires an additionally, dynamically-allocated structure to hold some internals) - you don't need to, as auto_ptr converts to shared_ptr, there is a special ctor taking an auto_ptr
Unfortunately for this particular facility, the converting constructor is explicit, so you can't do
f(new_<T>(a, b, c))
if f takes a shared_ptr<T>.
Then isn't it worth generalizing new_<> to all sorts of smart pointer and handles? Like: f( new_< any_smart_ptr<T> >(a, b, c)) ; As long as there's a way to get the 'element_type' from 'any_smart_ptr' it'll work. On top of that there could be the friendlier: make_auto_ptr, make_shared_ptr, etc... -- Fernando Cacciola SciSoft http://fcacciola.50webs.com/

Fernando Cacciola wrote:
David Abrahams wrote:
David Abrahams <dave@boost-consulting.com> writes:
Ulrich Eckhardt <doomster@knuut.de> writes:
On Wednesday 29 March 2006 08:05, Václav Veselý wrote:
I'm confused with syntax. new_<T> always creates auto_ptr<T>. How can I create for example shared_ptr<T>?
You probably can't, but: - std::auto_ptr is much less resource intensive (shared_ptr requires an additionally, dynamically-allocated structure to hold some internals) - you don't need to, as auto_ptr converts to shared_ptr, there is a special ctor taking an auto_ptr
Unfortunately for this particular facility, the converting constructor is explicit, so you can't do
f(new_<T>(a, b, c))
if f takes a shared_ptr<T>.
Then isn't it worth generalizing new_<> to all sorts of smart pointer and handles?
Like:
f( new_< any_smart_ptr<T> >(a, b, c)) ;
this might be worth persuing (see below)
As long as there's a way to get the 'element_type' from 'any_smart_ptr' it'll work.
On top of that there could be the friendlier: make_auto_ptr, make_shared_ptr, etc...
That would generate *a lot* of code to parse for the compiler. It is better that new_<T>(...) produces an unspecified type that has a templated conversion operator, eg.: template< class T > struct new_return { std::auto_ptr<T> new_; new_return( T* new_ ) : new_(new_) { } template< class SP > operator SP() const { return SP( new_.release() ); } operator std::auto_ptr<T>() const { return new_; } }; Or something -Thorsten

Thorsten Ottosen <thorsten.ottosen@dezide.com> writes:
It is better that new_<T>(...) produces an unspecified type that has a templated conversion operator, eg.:
template< class T > struct new_return { std::auto_ptr<T> new_;
new_return( T* new_ ) : new_(new_) { }
template< class SP > operator SP() const { return SP( new_.release() ); }
operator std::auto_ptr<T>() const { return new_; } };
Or something
I started down that road, but unfortunately, there's nothing to keep the operator SP conversion from converting to raw pointers. ;-) I don't see why Peter is the only one who seems to be responding to the auto_ptr rvalue implicit conversion technique. It just works. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Thorsten Ottosen <thorsten.ottosen@dezide.com> writes:
It is better that new_<T>(...) produces an unspecified type that has a templated conversion operator, eg.:
template< class T > struct new_return { std::auto_ptr<T> new_;
new_return( T* new_ ) : new_(new_) { }
template< class SP > operator SP() const { return SP( new_.release() ); }
operator std::auto_ptr<T>() const { return new_; } };
Or something
I started down that road, but unfortunately, there's nothing to keep the operator SP conversion from converting to raw pointers. ;-)
I don't see why Peter is the only one who seems to be responding to the auto_ptr rvalue implicit conversion technique. It just works.
Indeed. I agree is better than my initial proposal, and better than the parametrized conversion op, for the reason you just gave. Fernando Cacciola

Fernando Cacciola wrote:
David Abrahams wrote:
I don't see why Peter is the only one who seems to be responding to the auto_ptr rvalue implicit conversion technique. It just works.
BTW, I forgot to mention that new_ is a great idea! :) Many years ago there was a CUJ article presenting a Handle class which was essentially like a share_ptr(new_) but built in a single class. One of the strong points of that class was precisely that it totally avoided raw pointers. I liked it a lot and wonder ever since why it wasn't widely adopted. Now the "handle instead of pointer" paradigm can start to become best practice as it should have been for such a long time. -- Fernando Cacciola SciSoft http://fcacciola.50webs.com/

"Fernando Cacciola" <fernando_cacciola@hotmail.com> writes:
Fernando Cacciola wrote:
David Abrahams wrote:
I don't see why Peter is the only one who seems to be responding to the auto_ptr rvalue implicit conversion technique. It just works.
BTW, I forgot to mention that new_ is a great idea! :)
Thanks. The question now is whether this facility should be adopted by the smart pointer library or not, and that's up to Peter. If not, it will need a separate review. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
"Fernando Cacciola" <fernando_cacciola@hotmail.com> writes:
Fernando Cacciola wrote:
David Abrahams wrote:
I don't see why Peter is the only one who seems to be responding to the auto_ptr rvalue implicit conversion technique. It just works.
BTW, I forgot to mention that new_ is a great idea! :)
Thanks. The question now is whether this facility should be adopted by the smart pointer library or not, and that's up to Peter. If not, it will need a separate review.
We need documentation and tests as a first step. :-) (Regardless of whether new.hpp ends up in Smart_ptr or Utility.)

"Peter Dimov" <pdimov@mmltd.net> writes:
David Abrahams wrote:
"Fernando Cacciola" <fernando_cacciola@hotmail.com> writes:
Fernando Cacciola wrote:
David Abrahams wrote:
I don't see why Peter is the only one who seems to be responding to the auto_ptr rvalue implicit conversion technique. It just works.
BTW, I forgot to mention that new_ is a great idea! :)
Thanks. The question now is whether this facility should be adopted by the smart pointer library or not, and that's up to Peter. If not, it will need a separate review.
We need documentation and tests as a first step. :-) (Regardless of whether new.hpp ends up in Smart_ptr or Utility.)
Yes, but if it's going to go into Smart_ptr we need those things sooner :) So I'm still asking. I can _request_ a review, if necessary, without having written the tests and docs. But I'd rather not do it that way. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"Fernando Cacciola" <fernando_cacciola@hotmail.com> writes:
David Abrahams wrote:
David Abrahams <dave@boost-consulting.com> writes:
Ulrich Eckhardt <doomster@knuut.de> writes:
On Wednesday 29 March 2006 08:05, Václav Veselý wrote:
I'm confused with syntax. new_<T> always creates auto_ptr<T>. How can I create for example shared_ptr<T>?
You probably can't, but: - std::auto_ptr is much less resource intensive (shared_ptr requires an additionally, dynamically-allocated structure to hold some internals) - you don't need to, as auto_ptr converts to shared_ptr, there is a special ctor taking an auto_ptr
Unfortunately for this particular facility, the converting constructor is explicit, so you can't do
f(new_<T>(a, b, c))
if f takes a shared_ptr<T>.
Then isn't it worth generalizing new_<> to all sorts of smart pointer and handles?
Like:
f( new_< any_smart_ptr<T> >(a, b, c)) ;
As long as there's a way to get the 'element_type' from 'any_smart_ptr' it'll work.
Yes, as I wrote elsewhere in this thread, I did write a version that makes that possible.
On top of that there could be the friendlier: make_auto_ptr, make_shared_ptr, etc...
None of that is particularly friendly compared to new_<T>(a,b,c)... as long if we have the appropriate non-explicit converting constructor from auto_ptr rvalues in all the other smart pointers. Peter's already made that change to shared_ptr and I'm sure it's just a matter of time before that change is propagated to the other Boost smart pointers. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
On top of that there could be the friendlier: make_auto_ptr, make_shared_ptr, etc...
None of that is particularly friendly compared to new_<T>(a,b,c)... as long if we have the appropriate non-explicit converting constructor from auto_ptr rvalues in all the other smart pointers.
make_shared_ptr<T>( a, b, c ) (or however we end up calling it) still has the advantage of being able to fold the two allocations into one.

"Peter Dimov" <pdimov@mmltd.net> writes:
David Abrahams wrote:
On top of that there could be the friendlier: make_auto_ptr, make_shared_ptr, etc...
None of that is particularly friendly compared to new_<T>(a,b,c)... as long if we have the appropriate non-explicit converting constructor from auto_ptr rvalues in all the other smart pointers.
make_shared_ptr<T>( a, b, c ) (or however we end up calling it) still has the advantage of being able to fold the two allocations into one.
Very true; that's a good point. -- Dave Abrahams Boost Consulting www.boost-consulting.com

On 03/29/2006 05:05 PM, Peter Dimov wrote:
make_shared_ptr<T>( a, b, c ) (or however we end up calling it) still has the advantage of being able to fold the two allocations into one.
I assume the two allocations are for the detached refcount and the referent. Wouldn't placing them in the same block of memory with a single call to new (and I assume this is what you mean by "fold the two allocations into one") mean that shared_ptr would have to change it's two deletes (1 for refcount and 1 for referent) into one? IOW, make_shared_ptr would not make the current shared_ptr but another type of smart pointer. Or am I missing something?

Hi there, I'm very much in support of new_<>, it seems like an excellent addition. But... ...might it be better for new_<> to return a unique_ptr [1] rather than an auto_ptr? You'd get static custom deleter support (which shared_ptr could then store as a dynamic deleter) : shared_ptr<T> p = new_<T, null_deleter>(); (I'm not sure how you'd initialise the deleter) and array support as well : shared_array<T> collection = new_<T[10]>(); (you'd have to disable unique_ptr<T[]> conversion to shared_ptr, but allow shared_array conversion) Sam [1] N1856 <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1856.html#Addition%20-%20Class%20template%20unqiue_ptr> http://tinyurl.com/gchu9

"Sam Partington" <sam.partington@gmail.com> writes:
Hi there,
I'm very much in support of new_<>, it seems like an excellent addition. But...
...might it be better for new_<> to return a unique_ptr [1] rather than an auto_ptr?
In principle, yes. But unfortunately we have no smart pointers that can accept a unique_ptr as a ctor argument today, and we won't be able to change auto_ptr in that way, and anyway there's really nothing wrong with auto_ptr until it becomes an lvalue... so auto_ptr is the right choice in this case. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Larry Evans wrote:
On 03/29/2006 05:05 PM, Peter Dimov wrote:
make_shared_ptr<T>( a, b, c ) (or however we end up calling it) still has the advantage of being able to fold the two allocations into one.
I assume the two allocations are for the detached refcount and the referent. Wouldn't placing them in the same block of memory with a single call to new (and I assume this is what you mean by "fold the two allocations into one") mean that shared_ptr would have to change it's two deletes (1 for refcount and 1 for referent) into one?
Yes, the referent will be destroyed in place via explicit destructor call, not via delete.
IOW, make_shared_ptr would not make the current shared_ptr but another type of smart pointer. Or am I missing something?
The current shared_ptr can handle that.

I want to share my ideas inspired by this topic. 1) the naming scheme. I like naming after std:make_pair boost::make_ptr<object_type>(a, b, c); 2) this facility should provide a way to specify creation and deletion policies (malloc/free for example) It is absolutely must for real life programming. Consider dealing with windows API handlers in exception safe manner. it can be specified like boost::make_ptr<ptr_type>(boost::make_tuple(a, b, c), free, malloc); // all params (template and function arguments) are optional ptr_type defaults to std::unique_ptr reason: to support custom deletion in this case the type of object to be actually created is specified as a template parameter: boost::make_ptr<object_type>(boost::make_tuple(a, b, c), free, malloc); it means that this function needs to be specialized for each supported smart_ptr to avoid duplication like: boost::make_ptr< object_type, smart_ptr<object_type>
(boost::make_tuple(a, b, c), free, malloc);
this function is specialized for boost::shared_ptr (std::tr1::shared_ptr) to provide optimization like in make_shared_ptr<T>( a, b, c ) function mentioned by Peter Dimov creation/deletion defaults to new/delete unfortunately, we can not have overload of boost::make_ptr function that can be used in the simplest scenario: boost::make_ptr<object_type>(a, b, c); because the constructor of object_type can have a tuple as a first argument and/or function pointers as second/third, etc. conclusion: the make_tuple syntactic overhead couldn't be avoided. It is a consequence of providing creation/deletion policy. the "simplest" case would be: boost::make_ptr<object_type>(boost::make_tuple(a)); another possibility would be variations of the following approach: in advanced use case we'll have boost::make_ptr<ptr_type>(a, b, c)(free, malloc); or boost::make_ptr<ptr_type>(free, malloc)(a, b, c); if it would be easier to implement and in the simplest use case it would be boost::make_ptr<object_type>(a, b, c); It's a win, but it costs. Personally, the flexibility of the last approach with additional parentheses is very attractive for me. the syntactic overhead is minimal even in the most advanced case, and no overhead at all in simple cases. I can live with it. What about you? Best, Oleg Abrosimov.

Václav Veselý <vaclav.vesely@email.cz> writes:
Yes, I'm interested.
IMHO maximum arity of about 5 isn't too restrictive. Except of this I would like to have an alternative new_ with greater arity which use only const reference forwarding. Non-const referrence parameters can be explicitly wrapped with boost::ref.
I'm confused with syntax. new_<T> always creates auto_ptr<T>. How can I create for example shared_ptr<T>?
auto_ptr<T> is convertible to shared_ptr<T>, so the intention was that you would use that conversion. I have another version where you write new_<shared_ptr<T> >(....) but that just seemed syntactically heavy to little real benefit.
How this is related to auto_overhead?
I have no idea. Nobody ever explained auto_overhead to me in a way that I could understand. -- Dave Abrahams Boost Consulting www.boost-consulting.com

On 03/29/2006 07:12 AM, David Abrahams wrote:
Václav Veselý <vaclav.vesely@email.cz> writes: [snip]
How this is related to auto_overhead?
I have no idea. Nobody ever explained auto_overhead to me in a way that I could understand.
It's the class template version of your function template, new_. IOW, it's something like: template<class T> struct auto_new : private std::auto_ptr<T> { typedef std::auto_ptr<T> super_type; template< BOOST_PP_ENUM_PARAMS_Z(1, NEW_arity, class A) > auto_new( BOOST_FORWARD_ENUM_BINARY_PARAM_SEQ_R(1, NEW_constness, A, x) ) : super_type(new T(BOOST_PP_ENUM_PARAMS_Z(1, NEW_arity, x))) { } T* get(void) { return super_type::get();} }; (I may have misplaced a '(' or ',' or two. I hope you can see what I meant). Note there's no T* in any CTOR arguments (unless, of course, there's some T CTOR taking a T*). Also note, there's no Overhead<T> as there is in auto_overhead, but that's just another feature (designed to save the extra allocation needed shared_ptr) and wouldn't be relevant to this discussion, AFAICT. Is auto_overhead still unclear?

Larry Evans wrote:
It's the class template version of your function template, new_. IOW, it's something like: ... Note there's no T* in any CTOR arguments (unless, of course, there's some T CTOR taking a T*).
Also note, there's no Overhead<T> as there is in auto_overhead, but that's just another feature (designed to save the extra allocation needed shared_ptr) and wouldn't be relevant to this discussion, AFAICT.
Is auto_overhead still unclear?
I'm afraid I still don't grasp. In paticular I'm missing an motivating example (use case) of what can be done with auto_overhead but not with new_. Regards, Vaclav

On 03/30/2006 11:46 PM, Vaclav Vesely wrote:
Larry Evans wrote:
It's the class template version of your function template, new_. IOW, it's something like:
[snip]
I'm afraid I still don't grasp.
In paticular I'm missing an motivating example (use case) of what can be done with auto_overhead but not with new_.
Thanks Vaclev for replying. Obviously I'm having a problem explaining. I'm wondering if it's because my auto_overhead's purpose is different than new_'s. IOW new_'s purpose if for exception safety (AFAICT), but auto_overhead's purpose is: 1) Allow allocation of overhead (e.g. refcount ) at the same time as the T. 2) Never allow exposure of the raw T* to code outside of either auto_overhead or the receiving smart_ptr, whatever that may be. IOW, the smart_ptr, say sp<T,O>, where T is the referent type and O is the overhead type, would have, *intead* of a CTOR taking a raw T*: sp<T,O>::sp(T* a_T) : my_ovhd(new O) , my_ref(a_T) {...} or an auto_ptr: sp<T,O>::sp(std::auto_ptr<T> a_T) : my_ovhd(new O) , my_ref(a_T.release()) {...} a CTOR taking a auto_overhead: sp<T,O>::sp(auto_overhead<T,0>& a_ovhd) : my_ovhd(a_ovhd.overhead()) , my_ref(a_ovhd.release()) {...} <USE_CASE> Although new_ does satisfy purpose 2 above, it doesn't allow sp to satisfy purpose 2 above also because it would allow: T* a_Tptr=new T; //exposes raw pointer std::auto_ptr<T> a_Taptr(a_Tptr); sp<T,O> a_spT(a_Taptr); </USE_CASE> I'm not familiar enough with the exception safety issue to know whether auto_overhead could avoid that problem also, but I'm guessing it could. Is that any clearer? Please let me know because I obviously need to work on my communication skills :(

On 03/31/2006 04:05 AM, Larry Evans wrote: [snip]
IOW, the smart_ptr, say sp<T,O>, where T is the referent type and O is the overhead type, would have, *intead* of a CTOR taking a raw T*:
sp<T,O>::sp(T* a_T) : my_ovhd(new O) , my_ref(a_T) {...}
or an auto_ptr:
sp<T,O>::sp(std::auto_ptr<T> a_T) : my_ovhd(new O) , my_ref(a_T.release()) {...}
a CTOR taking a auto_overhead: ^should be: it would have a CTOR taking an auto_overhead:
sp<T,O>::sp(auto_overhead<T,0>& a_ovhd) ^should be: sp<T,O>::sp(auto_overhead<T,Ovhd>&a a_ovhd) : my_ovhd(a_ovhd.overhead()) , my_ref(a_ovhd.release()) {...} where Ovhd is something like:
template<class Referent> struct strong_weak_counts { unsigned strong_count; unsigned weak_count; }; or it could just contain a single count or just a mark bit for mark-sweep collection. However, I've looked more closely at the code, and I see referent_overhead_ptrs is being used. It's not apparent how that saves allocation time, but maybe I need to look closer. What I had in mind was using a pointer to: class overhead_referent_vals : public Overhead<Referent> , public Referent {...}; inside the sp<Referent,?>, i.e. overhead_referent_vals<Overhead<Referent>,Referent>* sp<Referent,Overhead>:: my_ptr; As you can see, overhead_referent_vals has the overhead at the start; hence, the sp<>.get() would just return a pointer the 2nd superclass. part, i.e. the Referent*. [snip]

David Abrahams wrote:
I've developed some facilities that perhaps ought to be developed into full-fledged libraries or components of existing Boost libraries. This work centers around my long-standing threat to build a library that could create smart pointers safely, without ever exposing a raw pointer to the user. For example,
#include <memory> #include <boost/shared_ptr.hpp> #include "new.hpp"
struct foo { foo(int&, char const*, std::auto_ptr<int> const&); };
std::auto_ptr<int> x(new_<int>(3)); std::auto_ptr<foo> y(new_<foo>(*x, "hello, world", x)); boost::shared_ptr<foo> z(new_<foo>(*x, (char const*)"hello, world", x));
To solve this problem correctly, it was necessary to address "the forwarding problem" (http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm). To that end, I developed some preprocessor macros that one can use to generate the necessary overload sets. Is there interest in adopting any of this code (enclosed), and if so, where should it go?
First, I'd like to say that I am very interested in this in a general way, as I frequently end up writing forwarding sets (although usually escape the forwarding problem). I'm not intimate with the pp library, but if I read your code correctly you have "solved the forwarding problem" by writing all the permutations of const and non const for each forwarding set. Is that correct? If not could you clarify for us non-PPers? Personally I don't like the "new_" name. but that's purely subjective. I think I'd prefer something more like, "atomic_new" (although that could also be a bit misleading), or that at least has something more descriptive to indicate what else is going on, other than just a bare trailing _. What about the array form? Would it be worth going as far as to add static functions to the boost smart pointer classes that do the same thing, and even hidding their raw pointer constructors away so they can only be created using the safer factory methods? Obviously that last step would break existing code, but it would only break the largely unsafe uses. Best regards, [)o IhIL..

Phil Nash <phil.nash.lists@gmail.com> writes:
First, I'd like to say that I am very interested in this in a general way, as I frequently end up writing forwarding sets (although usually escape the forwarding problem).
I'm not intimate with the pp library, but if I read your code correctly you have "solved the forwarding problem" by writing all the permutations of const and non const for each forwarding set. Is that correct? If not could you clarify for us non-PPers?
That is correct.
Personally I don't like the "new_" name. but that's purely subjective. I think I'd prefer something more like, "atomic_new" (although that could also be a bit misleading), or that at least has something more descriptive to indicate what else is going on, other than just a bare trailing _.
Come up with a better name and I'll consider it.
What about the array form?
IMO it's not a very important use case, but if we have to support it, it would have accept the T[N] form and return shared_array: new_<int[50]>(); interestingly, I think there's only a viable zero-argument form for this ctor, so forwarding may not be of interest (?)
Would it be worth going as far as to add static functions to the boost smart pointer classes that do the same thing,
Yes.
and even hidding their raw pointer constructors away so they can only be created using the safer factory methods?
Probably not.
Obviously that last step would break existing code, but it would only break the largely unsafe uses.
Except for the few safe ones :( -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Phil Nash <phil.nash.lists@gmail.com> writes:
What about the array form?
IMO it's not a very important use case, but if we have to support it, it would have accept the T[N] form and return shared_array:
new_<int[50]>();
interestingly, I think there's only a viable zero-argument form for this ctor, so forwarding may not be of interest (?)
Very true. For me, at least, that does kinda remove the need.
Would it be worth going as far as to add static functions to the boost smart pointer classes that do the same thing,
Yes.
and even hidding their raw pointer constructors away so they can only be created using the safer factory methods?
Probably not.
Obviously that last step would break existing code, but it would only break the largely unsafe uses.
Except for the few safe ones :(
I see that Larry has been talking about much the same thing. It wouldn't be the first time that boost has changed an interface to make it safer, even though it breaks older code. I suppose the difference is that the smart pointers are probably the most used classes of the whole of boost! It could be done in stages, with the raw pointer and maybe auto_ptr constructors retained but deprecated, and later removed. Not sure if that helps much. In any case, it would need to be put to some vote once some concrete proposals are in place. I'd still urge you not to rule it out at this stage. Thanks and regards, [)o IhIL..

Phil Nash <phil.nash.lists@gmail.com> writes:
David Abrahams wrote:
Phil Nash <phil.nash.lists@gmail.com> writes:
What about the array form?
IMO it's not a very important use case, but if we have to support it, it would have accept the T[N] form and return shared_array:
new_<int[50]>();
interestingly, I think there's only a viable zero-argument form for this ctor, so forwarding may not be of interest (?)
Very true. For me, at least, that does kinda remove the need.
Would it be worth going as far as to add static functions to the boost smart pointer classes that do the same thing,
Yes.
Oh, but wait: this could introduce a lot of code duplication. It would probably be better to make the new free function a friend if you want to do something like that.
and even hidding their raw pointer constructors away so they can only be created using the safer factory methods?
Probably not.
Obviously that last step would break existing code, but it would only break the largely unsafe uses.
Except for the few safe ones :(
I see that Larry has been talking about much the same thing. It wouldn't be the first time that boost has changed an interface to make it safer, even though it breaks older code. I suppose the difference is that the smart pointers are probably the most used classes of the whole of boost!
It could be done in stages, with the raw pointer and maybe auto_ptr constructors retained but deprecated, and later removed. Not sure if that helps much.
In any case, it would need to be put to some vote once some concrete proposals are in place. I'd still urge you not to rule it out at this stage.
It's not up to me; I don't maintain the smart pointer library. -- Dave Abrahams Boost Consulting www.boost-consulting.com

On 03/28/2006 05:45 PM, David Abrahams wrote:
I've developed some facilities that perhaps ought to be developed into full-fledged libraries or components of existing Boost libraries. This work centers around my long-standing threat to build a library that could create smart pointers safely, without ever exposing a raw pointer to the user. For example, [snip] std::auto_ptr<foo> y(new_<foo>(*x, "hello, world", x)); boost::shared_ptr<foo> z(new_<foo>(*x, (char const*)"hello, world", x));
My interpretation of "create smart pointers safely" is that there's no way the user can "accidentally" create a smart pointer which "originally" was a raw pointer. Since shared_ptr has a CTOR taking a raw pointer, this doesn't make shared_ptr "safe". OTOH, even if the raw pointer CTOR of shared_ptr were removed, there would still be the shared_ptr CTOR taking an auto_ptr, and since auto_ptr can be constructed from a raw pointer, this still would not make share_ptr "safe" since "origin" of the shared_ptr could come from a raw pointer. AFAICT, the only way a smart pointer can be made "safe" (as defined above) is to only allow CTOR arguments which are "safe" (w.r.t. raw pointer origination) themselves. And the only way to do that is provide a template class having a merge of the properties of auto_ptr and new_, IOW, it's derived from auto_ptr or contains an auto_ptr, and the CTOR interface is like the new_ CTOR interface. e.g. like the auto_overhead I'd mentioned before. Or maybe I'm misunderstanding your definition of "safe"?

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Larry Evans
Or maybe I'm misunderstanding your definition of "safe"?
One way that it is safer is in sequence points, e.g. f( shared_ptr<X>(new X(1, 2, 3)), shared_ptr<Y>(new Y(1, 2, 3)) ); f( make_shared_ptr<X>(1, 2, 3), make_shared_ptr<Y>(1, 2, 3) ); The latter is safe, whereas the former is not. Regards, Paul Mensonides

Paul Mensonides wrote:
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Larry Evans
Or maybe I'm misunderstanding your definition of "safe"?
One way that it is safer is in sequence points, e.g.
f( shared_ptr<X>(new X(1, 2, 3)), shared_ptr<Y>(new Y(1, 2, 3)) );
f( make_shared_ptr<X>(1, 2, 3), make_shared_ptr<Y>(1, 2, 3) );
The latter is safe, whereas the former is not.
Another great example of exception-unsafe code is std::map<std::string,boost::shared_ptr<T>> m; m["foo"] = boost::shared_ptr<T>( new T ); -Thorsten

On 03/29/2006 06:44 AM, Paul Mensonides wrote:
[mailto:boost-bounces@lists.boost.org] On Behalf Of Larry Evans
Or maybe I'm misunderstanding your definition of "safe"?
One way that it is safer is in sequence points, e.g.
f( shared_ptr<X>(new X(1, 2, 3)), shared_ptr<Y>(new Y(1, 2, 3)) );
f( make_shared_ptr<X>(1, 2, 3), make_shared_ptr<Y>(1, 2, 3) );
The latter is safe, whereas the former is not.
THanks, thar clears things up. I guess I should have remembered that post that started all this from Vaclav with subject: [shared_ptr] Best Practices - new_shared_ptr and that cited: http://www.gotw.ca/gotw/056.htm The phrase "exeption safe" would have helped my memory. I guess exception safety is why you use "sequence points" above, although I'm not very exception safe aware; so, I could be wrong. Maybe I should read more carefully Sutter's _Exceptional C++_ ?

Larry Evans <cppljevans@cox-internet.com> writes:
On 03/28/2006 05:45 PM, David Abrahams wrote:
I've developed some facilities that perhaps ought to be developed into full-fledged libraries or components of existing Boost libraries. This work centers around my long-standing threat to build a library that could create smart pointers safely, without ever exposing a raw pointer to the user. For example, [snip] std::auto_ptr<foo> y(new_<foo>(*x, "hello, world", x)); boost::shared_ptr<foo> z(new_<foo>(*x, (char const*)"hello, world", x));
My interpretation of "create smart pointers safely" is that there's no way the user can "accidentally" create a smart pointer which "originally" was a raw pointer. Since shared_ptr has a CTOR taking a raw pointer, this doesn't make shared_ptr "safe".
...
Or maybe I'm misunderstanding your definition of "safe"?
Deliberately, it seems. I didn't say I was ``making shared_ptr "safe".'' I said I was making it possible to create smart pointers safely. -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (12)
-
David Abrahams
-
Fernando Cacciola
-
Larry Evans
-
Oleg Abrosimov
-
Paul Mensonides
-
Peter Dimov
-
Phil Nash
-
Sam Partington
-
Thorsten Ottosen
-
Ulrich Eckhardt
-
Vaclav Vesely
-
Václav Veselý