Composing non copyable and movable classes

Hi, Imagine a class X which contains several noncopyable and movable members Ci mi;. class X : noncopyable { C1 m1; C2 m2; // ... Ck mk; // ... }; Very often a class is made movable by storing a pointer to some implementation data on a shared_ptr. Without allocator this means a heap new/delete. If the class accept allocators this means an allocate/deallocate on the allocator. class Mi : noncopyable { struct implementation_data; shared_ptr<implementation_data> data_; // ... }; The question is how to make X movable efficiently. The first way is to move each one of the movable objects one by one. This could be expensive if the number of members is high. In addition X can have also member that are not movable. The second approach is to use the same technique, store a pointer to the data on a shared_pointer, and move on one operation all the members via the pointer. class X : noncopyable { struct implementation_data { C1 m1; C1 m2; // ... C1 mk; }; shared_ptr<implementation_data> data_; public: // ... }; In this case the class X don't need any more that its members be movable. In order to avoid the allocation/deallocation of the respective classes Ci, it would be interesting to have a class which is not copyable and not movable and that owns the implementation class nonmovable_Ci : noncopyable { struct implementation_data; implementation_data data_; // ... }; Except the movable support, the nonmovable_Ci class share with Ci the same functionality. But is not related to Ci by inheritance. Both classes model a common concept. Of course this can not be applied if the implementation_data must be hidden in some .cpp file (pimpl idiom). The consequence is the classes and functions that should work with both implementations need to be templated. This allows the class X to allocate all the data at once. class X : noncopyable { struct implementation_data { nonmovable_C1 m1; nonmovable_C1 m2; // ... nonmovable_C1 mk; }; shared_ptr<implementation_data> data_; public: // ... }; I think that this separation is in line with the C++ principle "you don't pay for what you don't use". Any thoughts? Vicente

vicente.botet wrote:
The question is how to make X movable efficiently. The first way is to move each one of the movable objects one by one. This could be expensive if the number of members is high. In addition X can have also member that are not movable.
The second approach is to use the same technique, store a pointer to the data on a shared_pointer, and move on one operation all the members via the pointer.
Why shared_ptr? There is no sharing of ownership required. Just use std::unique_ptr.
I think that this separation is in line with the C++ principle "you don't pay for what you don't use". Any thoughts?
You're just stating the advantages and disadvantages between storing the data in the object itself and storing it in a separate object that is referenced. It's simply a tradeoff between {locality, compactness, allocation speed} and moving speed.

----- Original Message ----- From: "Mathias Gaunard" <mathias.gaunard@ens-lyon.org> To: <boost@lists.boost.org> Sent: Saturday, December 06, 2008 11:00 PM Subject: Re: [boost] Composing non copyable and movable classes
vicente.botet wrote:
The question is how to make X movable efficiently. The first way is to move each one of the movable objects one by one. This could be expensive if the number of members is high. In addition X can have also member that are not movable.
The second approach is to use the same technique, store a pointer to the data on a shared_pointer, and move on one operation all the members via the pointer.
Why shared_ptr? There is no sharing of ownership required. Just use std::unique_ptr.
Right, std::unique_ptr is enough.
I think that this separation is in line with the C++ principle "you don't pay for what you don't use". Any thoughts?
You're just stating the advantages and disadvantages between storing the data in the object itself and storing it in a separate object that is referenced.
It's simply a tradeoff between {locality, compactness, allocation speed} and moving speed.
If both classes were available, will you use the class Ci in contexts where nonmovable_Ci is more performant? Vicente

Vicente Botet wrote:
From: "Mathias Gaunard" <mathias.gaunard@ens-lyon.org>
vicente.botet wrote:
The question is how to make X movable efficiently. The first way is to move each one of the movable objects one by one. This could be expensive if the number of members is high. In addition X can have also member that are not movable.
The second approach is to use the same technique, store a pointer to the data on a shared_pointer, and move on one operation all the members via the pointer. Why shared_ptr? There is no sharing of ownership required. Just use std::unique_ptr.
Right, std::unique_ptr is enough.
Speaking of unique_ptr is it possible to get a C++03 emulation of unique_ptr into boost? I've been using http://home.twcny.rr.com/hinnant/cpp_extensions/unique_ptr_03.html for months and it seems to work fine. -- Michael Marcin

Michael Marcin:
Speaking of unique_ptr is it possible to get a C++03 emulation of unique_ptr into boost?
I've been using http://home.twcny.rr.com/hinnant/cpp_extensions/unique_ptr_03.html for months and it seems to work fine.
To get it into Boost, someone needs to contribute the tests and documentation and commit to maintaining it. :-) That said, I'm not fond of this property of the cited implementation: # This implementation will mistakenly move from const lvalues. const unique_ptr<int> cp; unique_ptr<int> p(cp); // should not compile -- Peter Dimov http://www.pdplayer.com

Peter Dimov wrote:
Michael Marcin:
Speaking of unique_ptr is it possible to get a C++03 emulation of unique_ptr into boost?
I've been using http://home.twcny.rr.com/hinnant/cpp_extensions/unique_ptr_03.html for months and it seems to work fine.
To get it into Boost, someone needs to contribute the tests and documentation and commit to maintaining it. :-)
That said, I'm not fond of this property of the cited implementation:
#
This implementation will mistakenly move from const lvalues. Howard has an updated version that doesn't do this. The new version is really slick.
Sebastian

On Dec 10, 2008, at 4:12 PM, Sebastian Redl wrote:
Peter Dimov wrote:
Michael Marcin:
Speaking of unique_ptr is it possible to get a C++03 emulation of unique_ptr into boost?
I've been using http://home.twcny.rr.com/hinnant/cpp_extensions/unique_ptr_03.html for months and it seems to work fine.
To get it into Boost, someone needs to contribute the tests and documentation and commit to maintaining it. :-)
That said, I'm not fond of this property of the cited implementation:
#
This implementation will mistakenly move from const lvalues. Howard has an updated version that doesn't do this. The new version is really slick.
Here's a sketch of what Sebastian is referring to. It isn't a complete unique_ptr. It is just a move-only-foundation to build off of. I'm afraid I don't have the time to contribute the tests and documentation and commit to maintaining a unique_ptr. So this is offered in the same spirt as the code at http://home.twcny.rr.com/hinnant/cpp_extensions/unique_ptr_03.html (anyone can do whatever they want with it). Also Sebastian deserves credit for some of the details below. template <class T> class unique_ptr { T* ptr_; unique_ptr(unique_ptr&); unique_ptr& operator=(unique_ptr&); class rv { unique_ptr& r_; public: explicit rv(unique_ptr& r) : r_(r) {} unique_ptr* operator->() {return &r_;} }; public: operator rv() {return rv(*this);} unique_ptr(rv r) : ptr_(r->release()) {} unique_ptr& operator=(rv r) {reset(r->release()); return *this;} friend unique_ptr move(unique_ptr& u) {return unique_ptr(rv(u));} friend unique_ptr move(rv r) {return unique_ptr(r);} explicit unique_ptr(T* p = 0) : ptr_(p) {} template <class U> unique_ptr(unique_ptr<U> u) : ptr_(u.release()) {} ~unique_ptr() {reset();} template <class U> unique_ptr& operator=(unique_ptr<U> u) {reset(u.release()); return *this;} T* release() { T* tmp = ptr_; ptr_ = 0; return tmp; } void reset(T* ptr = 0) { if (ptr_) delete ptr_; ptr_ = ptr; } }; It isn't a perfect emulation. But I think it is pretty close. And where it fails, it is not a dangerous failure like moving from a const. -Howard

From: boost-bounces@lists.boost.org [mailto:boost- bounces@lists.boost.org] On Behalf Of Howard Hinnant Sent: Wednesday, December 10, 2008 4:28 PM
Here's a sketch of what Sebastian is referring to. It isn't a complete unique_ptr. It is just a move-only-foundation to build off of.
... We've got a number of use-cases where unique_ptr would be extremely helpful. So, I figured I'd take a stab at a reasonable emulation and see how far I could get. I started playing around with Howard's skeleton to convince myself that I understood enough about rvalue references and move semantics to be able to finish the implementation. What I found, though, is that my understanding is lacking, particularly with regard to attempting to emulate the correct behavior in a C++03 world. Given Howard's skeleton, I tried the following test case: void f() { unique_ptr<int> p1(new int); unique_ptr<int> p2(p1); } This of course does not compile because there is no publically accessible constructor for unique_ptr that can do the job. The conversions and constructors using 'rv' aren't accessible either. In the end, I concluded that emulation on C++03 could provide move semantics, but that move operations need to be explicit, not implicit. That is, the test case should be: void f() { unique_ptr<int> p1(new int); unique_ptr<int> p2(move(p1)) } Is my conclusion correct? -Chris
template <class T> class unique_ptr { T* ptr_;
unique_ptr(unique_ptr&); unique_ptr& operator=(unique_ptr&);
class rv { unique_ptr& r_; public: explicit rv(unique_ptr& r) : r_(r) {} unique_ptr* operator->() {return &r_;} };
public:
operator rv() {return rv(*this);} unique_ptr(rv r) : ptr_(r->release()) {} unique_ptr& operator=(rv r) {reset(r->release()); return *this;} friend unique_ptr move(unique_ptr& u) {return unique_ptr(rv(u));} friend unique_ptr move(rv r) {return unique_ptr(r);}
explicit unique_ptr(T* p = 0) : ptr_(p) {} template <class U> unique_ptr(unique_ptr<U> u) : ptr_(u.release()) {} ~unique_ptr() {reset();}
template <class U> unique_ptr& operator=(unique_ptr<U> u) {reset(u.release()); return *this;}
T* release() { T* tmp = ptr_; ptr_ = 0; return tmp; }
void reset(T* ptr = 0) { if (ptr_) delete ptr_; ptr_ = ptr; } };
It isn't a perfect emulation. But I think it is pretty close. And where it fails, it is not a dangerous failure like moving from a const.

On Dec 18, 2008, at 10:22 AM, Chris Newbold wrote:
From: boost-bounces@lists.boost.org [mailto:boost- bounces@lists.boost.org] On Behalf Of Howard Hinnant Sent: Wednesday, December 10, 2008 4:28 PM
Here's a sketch of what Sebastian is referring to. It isn't a complete unique_ptr. It is just a move-only-foundation to build off of.
...
We've got a number of use-cases where unique_ptr would be extremely helpful. So, I figured I'd take a stab at a reasonable emulation and see how far I could get. I started playing around with Howard's skeleton to convince myself that I understood enough about rvalue references and move semantics to be able to finish the implementation.
What I found, though, is that my understanding is lacking, particularly with regard to attempting to emulate the correct behavior in a C++03 world.
Given Howard's skeleton, I tried the following test case:
void f() { unique_ptr<int> p1(new int); unique_ptr<int> p2(p1); }
This of course does not compile because there is no publically accessible constructor for unique_ptr that can do the job. The conversions and constructors using 'rv' aren't accessible either.
In the end, I concluded that emulation on C++03 could provide move semantics, but that move operations need to be explicit, not implicit. That is, the test case should be:
void f() { unique_ptr<int> p1(new int); unique_ptr<int> p2(move(p1)) }
Is my conclusion correct?
Your conclusion is correct. And this is not a problem. moves from lvalues (p1) should be explicit, even in C++0X. Here the emulation is perfect. Note that if p1 is an rvalue, the move is implicit, both emulated and in C++0X: unique_ptr<int> g(); void f() { unique_ptr<int> p2(g()); // implicit move, good } One place the emulation breaks down is inside of g(): unique_ptr<int> g() { unique_ptr<int> p; // ... return move(p); // ! explicit move necessary for emulation, unwanted in C++0X } In C++0X one should say: return p; instead. Putting the "move" on the return statement will inhibit rvo, probably making your code slower. But in C++03 emulation, the move is required, else you get a compile time error. If you can build the return value in the return statement, then both C++03 emulation and C+ +0X are identical: unique_ptr<int> g() { return unique_ptr<int>(); // implicit move, good } Another currently weak place in the emulation is that "move" is injected as a friend of the move-only class. In C++0X, move is a std::lib function, not to be implemented by user code. And user code should always call std::move(x), not move(x). -Howard
-Chris
template <class T> class unique_ptr { T* ptr_;
unique_ptr(unique_ptr&); unique_ptr& operator=(unique_ptr&);
class rv { unique_ptr& r_; public: explicit rv(unique_ptr& r) : r_(r) {} unique_ptr* operator->() {return &r_;} };
public:
operator rv() {return rv(*this);} unique_ptr(rv r) : ptr_(r->release()) {} unique_ptr& operator=(rv r) {reset(r->release()); return *this;} friend unique_ptr move(unique_ptr& u) {return unique_ptr(rv(u));} friend unique_ptr move(rv r) {return unique_ptr(r);}
explicit unique_ptr(T* p = 0) : ptr_(p) {} template <class U> unique_ptr(unique_ptr<U> u) : ptr_(u.release()) {} ~unique_ptr() {reset();}
template <class U> unique_ptr& operator=(unique_ptr<U> u) {reset(u.release()); return *this;}
T* release() { T* tmp = ptr_; ptr_ = 0; return tmp; }
void reset(T* ptr = 0) { if (ptr_) delete ptr_; ptr_ = ptr; } };
It isn't a perfect emulation. But I think it is pretty close. And where it fails, it is not a dangerous failure like moving from a const.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Howard Hinnant wrote:
On Dec 18, 2008, at 10:22 AM, Chris Newbold wrote:
In the end, I concluded that emulation on C++03 could provide move semantics, but that move operations need to be explicit, not implicit. That is, the test case should be:
void f() { unique_ptr<int> p1(new int); unique_ptr<int> p2(move(p1)) }
Is my conclusion correct?
Your conclusion is correct. And this is not a problem. moves from lvalues (p1) should be explicit, even in C++0X. Here the emulation is perfect.
Note that if p1 is an rvalue, the move is implicit, both emulated and in C++0X:
unique_ptr<int> g();
void f() { unique_ptr<int> p2(g()); // implicit move, good }
One place the emulation breaks down is inside of g():
unique_ptr<int> g() { unique_ptr<int> p; // ... return move(p); // ! explicit move necessary for emulation, unwanted in C++0X }
Another currently weak place in the emulation is that "move" is injected as a friend of the move-only class. In C++0X, move is a std::lib function, not to be implemented by user code. And user code should always call std::move(x), not move(x).
Another weakness - at least in my implementation - is that implicit conversions on return don't work. This is not a problem for unique_ptr, really, but it is for my more complex pointers. OwningStmtResult foo() { return true; // OwningStmtResult has an implicit conversion from bool. } This fails to compile. Sebastian

on Thu Dec 18 2008, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
Another weakness - at least in my implementation - is that implicit conversions on return don't work. This is not a problem for unique_ptr, really, but it is for my more complex pointers.
OwningStmtResult foo() { return true; // OwningStmtResult has an implicit conversion from bool. }
This fails to compile.
IIRC, if you look around the web you can find an implementation of "move_ptr" by Jonathan Turkanis that does a perfect emulation. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

I tried to locate the mentioned unique_ptr<> classes for testing, but am unable to locate them. Can somebody point me in the right direction please? Many thanks, -Sid Sacek

On Sat, Dec 20, 2008 at 3:10 PM, Sid Sacek <heavyreign@optonline.net> wrote:
I tried to locate the mentioned unique_ptr<> classes for testing, but am unable to locate them.
Can somebody point me in the right direction please?
This is the only unique_ptr I know of: http://www.boost.org/doc/libs/1_35_0/doc/html/interprocess/interprocess_smar...

on Sat Dec 06 2008, "vicente.botet" <vicente.botet-AT-wanadoo.fr> wrote:
Hi,
Imagine a class X which contains several noncopyable and movable members Ci mi;.
class X : noncopyable { C1 m1; C2 m2; // ... Ck mk; // ... };
Very often a class is made movable by storing a pointer to some implementation data on a shared_ptr. Without allocator this means a heap new/delete. If the class accept allocators this means an allocate/deallocate on the allocator.
class Mi : noncopyable { struct implementation_data; shared_ptr<implementation_data> data_; // ... };
The question is how to make X movable efficiently. The first way is to move each one of the movable objects one by one. This could be expensive if the number of members is high.
In general it isn't high enough to justify doing anything else.
In addition X can have also member that are not movable.
Do you mean "copiable but not otherwise movable,' or simply not movable?
The second approach is to use the same technique, store a pointer to the data on a shared_pointer, and move on one operation all the members via the pointer.
class X : noncopyable { struct implementation_data { C1 m1; C1 m2; // ... C1 mk; }; shared_ptr<implementation_data> data_; public: // ... };
In this case the class X don't need any more that its members be movable. In order to avoid the allocation/deallocation of the respective classes Ci, it would be interesting to have a class which is not copyable and not movable and that owns the implementation
class nonmovable_Ci : noncopyable { struct implementation_data; implementation_data data_; // ... };
To justify that, moving one of the C's must be really expensive, or you have to expect many, many moves of each object to balance the cost of allocation. In general, I'd consider using the small object optimization to handle cases like this.
Except the movable support, the nonmovable_Ci class share with Ci the same functionality. But is not related to Ci by inheritance. Both classes model a common concept. Of course this can not be applied if the implementation_data must be hidden in some .cpp file (pimpl idiom).
This feels like overgeneralizing. Unless otherwise justified, every type should have value semantics, i.e., should be movable and copiable.
The consequence is the classes and functions that should work with both implementations need to be templated.
This allows the class X to allocate all the data at once.
class X : noncopyable { struct implementation_data { nonmovable_C1 m1; nonmovable_C1 m2; // ... nonmovable_C1 mk; }; shared_ptr<implementation_data> data_; public: // ... };
I think that this separation is in line with the C++ principle "you don't pay for what you don't use". Any thoughts?
I think it makes me pay for a lot of complexity and for the cost of managing dynamically-allocated resources. But that's just an initial reaction. I'm not really sure I understand the context for what you're proposing. Are you saying all classes should be designed this way, or... ? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Sat, Dec 6, 2008 at 4:42 PM, David Abrahams <dave@boostpro.com> wrote:
This feels like overgeneralizing. Unless otherwise justified, every type should have value semantics, i.e., should be movable and copiable.
I find C-style incomplete type (and therefore non-copyable, non-movable and non-value) handles quite practical too: //foo.h namespace boost { template <class> class shared_ptr; } struct handle; boost::shared_ptr<handle> create_handle(); void do_something( handle & ); Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

on Sun Dec 07 2008, "Emil Dotchevski" <emil-AT-revergestudios.com> wrote:
On Sat, Dec 6, 2008 at 4:42 PM, David Abrahams <dave@boostpro.com> wrote:
This feels like overgeneralizing. Unless otherwise justified, every type should have value semantics, i.e., should be movable and copiable.
I find C-style incomplete type (and therefore non-copyable, non-movable and non-value) handles quite practical too:
//foo.h namespace boost { template <class> class shared_ptr; } struct handle; boost::shared_ptr<handle> create_handle(); void do_something( handle & );
The type playing the role of handle in this case (at least according to the traditional meaning of "handle") is shared_ptr<handle>, which is both movable and copyable. Also the incompleteness of the struct is usually "otherwise justified" in such a case. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

----- Original Message ----- From: "David Abrahams" <dave@boostpro.com> To: <boost@lists.boost.org> Sent: Sunday, December 07, 2008 1:42 AM Subject: Re: [boost] Composing non copyable and movable classes
on Sat Dec 06 2008, "vicente.botet" <vicente.botet-AT-wanadoo.fr> wrote:
The question is how to make X movable efficiently. The first way is to move each one of the movable objects one by one. This could be expensive if the number of members is high.
In general it isn't high enough to justify doing anything else.
Right, in general. But if the class C has an array of Ci and the array size is known at runtime or as a template parameter.
In addition X can have also member that are not movable.
Do you mean "copiable but not otherwise movable,' or simply not movable?
The case that impose the allocation is a member not copyable nor movable.
In this case the class X don't need any more that its members be movable. In order to avoid the allocation/deallocation of the respective classes Ci, it would be interesting to have a class which is not copyable and not movable and that owns the implementation
class nonmovable_Ci : noncopyable { struct implementation_data; implementation_data data_; // ... };
To justify that, moving one of the C's must be really expensive, or you have to expect many, many moves of each object to balance the cost of allocation. In general, I'd consider using the small object optimization to handle cases like this.
If you are the designer of the class C, how can you consider using the small object optimization if you don't owns the classes Ci.
Except the movable support, the nonmovable_Ci class share with Ci the same functionality. But is not related to Ci by inheritance. Both classes model a common concept. Of course this can not be applied if the implementation_data must be hidden in some .cpp file (pimpl idiom).
This feels like overgeneralizing. Unless otherwise justified, every type should have value semantics, i.e., should be movable and copiable.
nonmovable_Ci could also be copyable and movable, call them other_Ci, but these operations could be expensive, i.e. other_Ci is intended to not be copied nor moved.
This allows the class X to allocate all the data at once.
I think that this separation is in line with the C++ principle "you don't pay for what you don't use". Any thoughts?
I think it makes me pay for a lot of complexity and for the cost of managing dynamically-allocated resources. But that's just an initial reaction. I'm not really sure I understand the context for what you're proposing. Are you saying all classes should be designed this way, or... ?
Not at all. If Ci and other_Ci were available, I would use other_Ci in this case. Now if I were the author of Ci I'll provide other_Ci only if I had a concrete use case, because maintainng both classes synchronized adds complexity. How these classes, Ci and other_Ci, can be designed is another question. Vicente

on Sun Dec 07 2008, "vicente.botet" <vicente.botet-AT-wanadoo.fr> wrote:
----- Original Message ----- From: "David Abrahams" <dave@boostpro.com> To: <boost@lists.boost.org> Sent: Sunday, December 07, 2008 1:42 AM Subject: Re: [boost] Composing non copyable and movable classes
on Sat Dec 06 2008, "vicente.botet" <vicente.botet-AT-wanadoo.fr> wrote:
The question is how to make X movable efficiently. The first way is to move each one of the movable objects one by one. This could be expensive if the number of members is high.
In general it isn't high enough to justify doing anything else.
Right, in general. But if the class C has an array of Ci and the array size is known at runtime or as a template parameter.
... then ... ?
In addition X can have also member that are not movable.
Do you mean "copiable but not otherwise movable,' or simply not movable?
The case that impose the allocation is a member not copyable nor movable.
In this case the class X don't need any more that its members be movable. In order to avoid the allocation/deallocation of the respective classes Ci, it would be interesting to have a class which is not copyable and not movable and that owns the implementation
class nonmovable_Ci : noncopyable { struct implementation_data; implementation_data data_; // ... };
To justify that, moving one of the C's must be really expensive, or you have to expect many, many moves of each object to balance the cost of allocation. In general, I'd consider using the small object optimization to handle cases like this.
If you are the designer of the class C, how can you consider using the small object optimization if you don't owns the classes Ci.
I don't understand the question. You just do it. See boost::function.
Except the movable support, the nonmovable_Ci class share with Ci the same functionality. But is not related to Ci by inheritance. Both classes model a common concept. Of course this can not be applied if the implementation_data must be hidden in some .cpp file (pimpl idiom).
This feels like overgeneralizing. Unless otherwise justified, every type should have value semantics, i.e., should be movable and copiable.
nonmovable_Ci could also be copyable and movable, call them other_Ci, but these operations could be expensive, i.e. other_Ci is intended to not be copied nor moved.
Again, this sounds like overgeneralizing. There's no point in optimizing for use cases that might not even exist. Move should never be expensive. If you have an expensive move, you've almost certainly done something wrong. -- Dave Abrahams BoostPro Computing http://www.boostpro.com
participants (12)
-
Chris Newbold
-
David Abrahams
-
Emil Dotchevski
-
Howard Hinnant
-
Mathias Gaunard
-
Michael Marcin
-
OvermindDL1
-
Peter Dimov
-
Sebastian Redl
-
Sid Sacek
-
Vicente Botet
-
vicente.botet