
Hi, In Best Practices section of shared_ptr is an example: void f(shared_ptr<int>, int); int g(); void ok() { shared_ptr<int> p(new int(2)); f(p, g()); } void bad() { f(shared_ptr<int>(new int(2)), g()); } Best Practices refers to http://www.gotw.ca/gotw/056.htm (it deals with auto_ptr, but for shared_ptr the conclusion is same). The article proposes following solution: template<typename T, typename A1> shared_ptr<Type> new_shared_ptr(A1 a1) { return shared_ptr<Type>(new Type(a1)); } void ok2() { f(new_shared_ptr<int>(2), g()); } However the conclusion of the article is, that ok1() is better than ok2() because you have to write helper functions. I don't agree. IMHO ok2 IS better and the helper functions should be in the library (so the uses doesn't have to write them. What do you think? Regards, Vaclav

Vaclav Vesely wrote:
However the conclusion of the article is, that ok1() is better than ok2() because you have to write helper functions. I don't agree. IMHO ok2 IS better and the helper functions should be in the library (so the uses doesn't have to write them. What do you think?
FWIW, as someone that wasn't entirely clear on the problem before reading the linked gotw article, I tend to agree that the helper function is preferable and would be a good inclusion into boost (with versions for each of the boost smart pointer types, of course). AFAICS it compiles away to little more than an enforcement on sequence ordering (at least no more than the two step recommendation), but retains the expressiveness of the original intent. I'd say it could be argued to be less preferable if you have to write the helper yourself every time - but if included in boost (and perhaps later, the standard) I can't see why it would be preferable to *not* at least have the option. Also, there are some (minor) incoveniences to the two step approach. Other than the obvious fact that there is more typing involved, there is also the issue of scope. If you bring a named instance of an auto_ptr (or shared_ptr etc) into being, it will "hang around" until the end of the scope (even if now empty, in the case of auto_ptr). This may not be desirable, in which case you have to introduce an extra scope to contain it, which seems like more and more clutter to our "workaround". Finally, even if a future standard "plugs" the loophole - this technique would still make code safer for older compilers. So, yes, I agree that some sort of smart pointer factory methods would be ideal. Best regards, [)o IhIL..

Vaclav Vesely <vaclav.vesely@email.cz> writes:
However the conclusion of the article is, that ok1() is better than ok2() because you have to write helper functions. I don't agree. IMHO ok2 IS better and the helper functions should be in the library (so the uses doesn't have to write them. What do you think?
Yes, the library ought to contain something like that, as I have been saying for years. Probably it should be spelled something like: new_<shared_ptr<T> >(a, b, c, ...) new_<auto_ptr<T> >(a, b, c, ...) Unfortunately, really making these functions work correctly for more than about 5 or 6 arguments is currently not feasible because of what's known as "the forwarding problem" (google it). Boost Consulting is working on a project where we'll need this functionality, so I hacked up the enclosed. I'm going to generalize it (and hopefully, optimize compilation speed) a bit and try to get it into Boost. In the meantime, enjoy. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Yes, the library ought to contain something like that, as I have been saying for years. Probably it should be spelled something like:
new_<shared_ptr<T> >(a, b, c, ...)
new_<auto_ptr<T> >(a, b, c, ...)
Unfortunately, really making these functions work correctly for more than about 5 or 6 arguments is currently not feasible because of what's known as "the forwarding problem" (google it).
Boost Consulting is working on a project where we'll need this functionality, so I hacked up the enclosed. I'm going to generalize it (and hopefully, optimize compilation speed) a bit and try to get it into Boost. In the meantime, enjoy.
IMHO const reference forwarding and explicit wrapping of non-const reference parameters with boost::ref is comfortable enough and feasible with current compilers. See the attachment. If there will be there a will to add my implementation into Boost, I will write an appendix to the Best Practices. Regards, Vaclav #define BOOST_TEST_MAIN #include <boost/intrusive_ptr.hpp> #include <boost/mpl/list.hpp> #include <boost/new.hpp> #include <boost/noncopyable.hpp> #include <boost/ref.hpp> #include <boost/shared_ptr.hpp> #include <boost/test/test_case_template.hpp> #include <boost/test/unit_test.hpp> #include <string> //----------------------------------------------------------------------------- static int g3 = 0; static int g5 = 0; class X: boost::noncopyable { public: X( int a1 = 0, int* a2 = NULL, int& a3 = g3, std::string const& a4 = "", const int& a5 = g5, int a6 = 0, int a7 = 0, int a8 = 0, int a9 = 0, int a10 = 0) : m_ref_count(0), m1(a1), m2(a2), m3(a3), m4(a4), m5(a5), m6(a6), m7(a7), m8(a8), m9(a9), m10(a10) { } int m_ref_count; int m1; int* m2; int& m3; std::string m4; const int& m5; int m6; int m7; int m8; int m9; int m10; }; void intrusive_ptr_add_ref(X* x) { ++(x->m_ref_count); } void intrusive_ptr_release(X* x) { if(--(x->m_ref_count) == 0) delete x; } //----------------------------------------------------------------------------- typedef boost::mpl::list< boost::intrusive_ptr<X>, boost::shared_ptr<X>
test_types;
BOOST_AUTO_TEST_CASE_TEMPLATE(new_test, PtrType, test_types) { int a1 = 1; int* a2 = new int(2); int& a3 = a1; std::string a4("4"); int a5 = 5; int a6 = 6; int a7 = 7; int a8 = 8; int a9 = 9; int a10 = 10; PtrType sp; sp = boost::new_<PtrType>(); BOOST_CHECK(sp); sp = boost::new_<PtrType>(a1); BOOST_CHECK(sp); BOOST_CHECK_EQUAL(sp->m1, a1); sp = boost::new_<PtrType>(a1, a2); BOOST_CHECK(sp); BOOST_CHECK_EQUAL(sp->m1, a1); BOOST_CHECK_EQUAL(sp->m2, a2); sp = boost::new_<PtrType>(a1, a2, boost::ref(a3)); BOOST_CHECK(sp); BOOST_CHECK_EQUAL(sp->m1, a1); BOOST_CHECK_EQUAL(sp->m2, a2); BOOST_CHECK_EQUAL(&sp->m3, &a3); sp = boost::new_<PtrType>(a1, a2, boost::ref(a3), a4); BOOST_CHECK(sp); BOOST_CHECK_EQUAL(sp->m1, a1); BOOST_CHECK_EQUAL(sp->m2, a2); BOOST_CHECK_EQUAL(&sp->m3, &a3); BOOST_CHECK_EQUAL(sp->m4, a4); sp = boost::new_<PtrType>(a1, a2, boost::ref(a3), a4, a5); BOOST_CHECK(sp); BOOST_CHECK_EQUAL(sp->m1, a1); BOOST_CHECK_EQUAL(sp->m2, a2); BOOST_CHECK_EQUAL(&sp->m3, &a3); BOOST_CHECK_EQUAL(sp->m4, a4); BOOST_CHECK_EQUAL(&sp->m5, &a5); sp = boost::new_<PtrType>(a1, a2, boost::ref(a3), a4, a5, a6); BOOST_CHECK(sp); BOOST_CHECK_EQUAL(sp->m1, a1); BOOST_CHECK_EQUAL(sp->m2, a2); BOOST_CHECK_EQUAL(&sp->m3, &a3); BOOST_CHECK_EQUAL(sp->m4, a4); BOOST_CHECK_EQUAL(&sp->m5, &a5); BOOST_CHECK_EQUAL(sp->m6, a6); sp = boost::new_<PtrType>(a1, a2, boost::ref(a3), a4, a5, a6, a7); BOOST_CHECK(sp); BOOST_CHECK_EQUAL(sp->m1, a1); BOOST_CHECK_EQUAL(sp->m2, a2); BOOST_CHECK_EQUAL(&sp->m3, &a3); BOOST_CHECK_EQUAL(sp->m4, a4); BOOST_CHECK_EQUAL(&sp->m5, &a5); BOOST_CHECK_EQUAL(sp->m6, a6); BOOST_CHECK_EQUAL(sp->m7, a7); sp = boost::new_<PtrType>(a1, a2, boost::ref(a3), a4, a5, a6, a7, a8); BOOST_CHECK(sp); BOOST_CHECK_EQUAL(sp->m1, a1); BOOST_CHECK_EQUAL(sp->m2, a2); BOOST_CHECK_EQUAL(&sp->m3, &a3); BOOST_CHECK_EQUAL(sp->m4, a4); BOOST_CHECK_EQUAL(&sp->m5, &a5); BOOST_CHECK_EQUAL(sp->m6, a6); BOOST_CHECK_EQUAL(sp->m7, a7); BOOST_CHECK_EQUAL(sp->m8, a8); sp = boost::new_<PtrType>(a1, a2, boost::ref(a3), a4, a5, a6, a7, a8, a9); BOOST_CHECK(sp); BOOST_CHECK_EQUAL(sp->m1, a1); BOOST_CHECK_EQUAL(sp->m2, a2); BOOST_CHECK_EQUAL(&sp->m3, &a3); BOOST_CHECK_EQUAL(sp->m4, a4); BOOST_CHECK_EQUAL(&sp->m5, &a5); BOOST_CHECK_EQUAL(sp->m6, a6); BOOST_CHECK_EQUAL(sp->m7, a7); BOOST_CHECK_EQUAL(sp->m8, a8); BOOST_CHECK_EQUAL(sp->m9, a9); sp = boost::new_<PtrType>(a1, a2, boost::ref(a3), a4, a5, a6, a7, a8, a9, a10); BOOST_CHECK(sp); BOOST_CHECK_EQUAL(sp->m1, a1); BOOST_CHECK_EQUAL(sp->m2, a2); BOOST_CHECK_EQUAL(&sp->m3, &a3); BOOST_CHECK_EQUAL(sp->m4, a4); BOOST_CHECK_EQUAL(&sp->m5, &a5); BOOST_CHECK_EQUAL(sp->m6, a6); BOOST_CHECK_EQUAL(sp->m7, a7); BOOST_CHECK_EQUAL(sp->m8, a8); BOOST_CHECK_EQUAL(sp->m9, a9); BOOST_CHECK_EQUAL(sp->m10, a10); delete a2; } //----------------------------------------------------------------------------- #ifndef BOOST_NEW_HPP_INCLUDED #define BOOST_NEW_HPP_INCLUDED #include <boost/config.hpp> #include <boost/preprocessor/repetition/enum_binary_params.hpp> #include <boost/preprocessor/repetition/enum_trailing_params.hpp> #include <boost/preprocessor/repetition/enum_params.hpp> #include <boost/preprocessor/repetition/repeat.hpp> namespace boost { #if !defined(BOOST_MAX_NEW_ARITY) #define BOOST_MAX_NEW_ARITY 10 #endif #define BOOST_NEW_TEMPLATE(z, n, d) \ template<typename T BOOST_PP_ENUM_TRAILING_PARAMS(n, typename A)> \ T new_(BOOST_PP_ENUM_BINARY_PARAMS(n, A, const& a)) \ { \ return T(new T::element_type(BOOST_PP_ENUM_PARAMS(n, a))); \ } \ /**/ BOOST_PP_REPEAT(BOOST_PP_INC(BOOST_MAX_NEW_ARITY), BOOST_NEW_TEMPLATE, BOOST_PP_EMPTY()) #undef BOOST_NEW_TEMPLATE } // namespace boost #endif // #ifndef BOOST_NEW_HPP_INCLUDED

On 03/26/2006 01:19 PM, Vaclav Vesely wrote: [snip]
IMHO const reference forwarding and explicit wrapping of non-const reference parameters with boost::ref is comfortable enough and feasible with current compilers. See the attachment.
If there will be there a will to add my implementation into Boost, I will write an appendix to the Best Practices.
Regards, Vaclav [snip] namespace boost {
#if !defined(BOOST_MAX_NEW_ARITY) #define BOOST_MAX_NEW_ARITY 10 #endif
#define BOOST_NEW_TEMPLATE(z, n, d) \ template<typename T BOOST_PP_ENUM_TRAILING_PARAMS(n, typename A)> \ T new_(BOOST_PP_ENUM_BINARY_PARAMS(n, A, const& a)) \ { \ return T(new T::element_type(BOOST_PP_ENUM_PARAMS(n, a))); \ } \ /**/
BOOST_PP_REPEAT(BOOST_PP_INC(BOOST_MAX_NEW_ARITY), BOOST_NEW_TEMPLATE, BOOST_PP_EMPTY())
#undef BOOST_NEW_TEMPLATE
} // namespace boost
This would not be useful for a smart pointer: template<class Referent> struct only_heap_ptrs ; which didn't have a CTOR taking a Referent* argument. IOW, there would be no: template<class Referent> only_heap_ptrs<Referent>::only_heap_ptrs(Referent* a_heap_ptr); Such a smart pointer would avoid a mistake like: int i; only_heap_ptrs<int> pi(&i); OTOH, this was one of the purposes of the auto_overhead template class of http://tinyurl.com/f2r7h. OTOH, it's very easy to reuse auto_overhead to write new_ AFAICT.

"Vaclav Vesely" <vaclav.vesely@email.cz> wrote in message news:e00i2r$6a8$1@sea.gmane.org...
Hi,
In Best Practices section of shared_ptr is an example:
void f(shared_ptr<int>, int); int g();
void ok() { shared_ptr<int> p(new int(2)); f(p, g()); }
void bad() { f(shared_ptr<int>(new int(2)), g()); }
Best Practices refers to http://www.gotw.ca/gotw/056.htm (it deals with auto_ptr, but for shared_ptr the conclusion is same). The article proposes following solution:
template<typename T, typename A1> shared_ptr<Type> new_shared_ptr(A1 a1) { return shared_ptr<Type>(new Type(a1)); }
void ok2() { f(new_shared_ptr<int>(2), g()); }
Another possible advantage of new_shared_ptr mentioned by the article is that it could reduce the number of news required to create the shared_ptr from 2 to 1. In the code shared_ptr<int> p(new int(7)); new is called twice: when the int is created and inside the shared_ptr's constructor to construct an object of a type inheriting from boost::sp_counted_base. The constructor is careful to maintain exception safety, but some people might find the two calls to new to be inefficient. The article suggested that new_shared_ptr could create an object containing an int and an intrusive count, so that it would be unnecessary to call new to create the int pointer. It would be possible to do this for boost::new_shared_ptr<T>, with a few minor complications. Since boost::shared_ptr's separate the destruction of the owned T pointer from the destruction of the shared_ptr infrastructure in order to allow for weak_ptrs, the T would have to be created using placement new, so that it would be possible to destroy it using placement delete when the last shared_ptr is destructed. Joe Gottman

On 03/24/2006 06:43 PM, Joe Gottman wrote: [snip]
Another possible advantage of new_shared_ptr mentioned by the article is that it could reduce the number of news required to create the shared_ptr from 2 to 1. In the code shared_ptr<int> p(new int(7)); new is called twice: when the int is created and inside the shared_ptr's constructor to construct an object of a type inheriting from boost::sp_counted_base. The constructor is careful to maintain exception safety, but some people might find the two calls to new to be inefficient. The article suggested that new_shared_ptr could create an object containing an int and an intrusive count, so that it would be unnecessary to call new to create the int pointer. [snip] Avoiding 2 new calls is one of the applications for the:
template<typename T, template<typename>class >OverHead auto_overhead; template mentioned in the post I bcc'ed to David. I bcc'ed because I was having trouble posting. It wasn't mentioned in http://article.gmane.org/gmane.comp.lib.boost.devel/140039 so I'll repeat it here:
The auto_overhead<Type,template<typename>class Overhead> template class in:
does *almost* this. If the Overhead<Type> were an empty class (i.e. no members) then it would essentially create a Type* with the given arguments. Then new_shared_ptr would simply create an auto_overhead<Type,...> and pass the Type* (using auto_overhead::referent()) to the share_ptr<Type> CTOR. IOW, instead of:
return shared_ptr<Type>(new Type(a1))
there would be:
return shared_ptr<Type>(auto_overhead<Type,Empty>().referent());
The Overhead<Type> could create, with a single call to new, an object containing a refcount followed by Type. The auto_overhead::reference() returns the Type* while the auto_overhead::overhead() returns overhead_type* where overhead_type could be a refcount or any other overhead, including strong_count and weak_count, depending on the Overhead template argument.

Joe Gottman wrote:
"Vaclav Vesely" <vaclav.vesely@email.cz> wrote in message news:e00i2r$6a8$1@sea.gmane.org...
[...]
void ok2() { f(new_shared_ptr<int>(2), g()); }
Another possible advantage of new_shared_ptr mentioned by the article is that it could reduce the number of news required to create the shared_ptr from 2 to 1. In the code shared_ptr<int> p(new int(7)); new is called twice: when the int is created and inside the shared_ptr's constructor to construct an object of a type inheriting from boost::sp_counted_base.
This is also what N1851 proposes, with a different spelling: make_shared_object, make_shared_object_with_alloc, make_shared_array. It's an additional argument in favor of new_shared_ptr instead of new_< shared_ptr >, if nothing else. There's also the alternative of shared_ptr<X>::create( a1, ..., aN ).
participants (6)
-
David Abrahams
-
Joe Gottman
-
Larry Evans
-
Peter Dimov
-
Phil Nash
-
Vaclav Vesely