From 63a822490bb1ac98d4fb8a1e76187ecd3ae5c118 Mon Sep 17 00:00:00 2001 From: "ajwong@chromium.org" Date: Fri, 9 Dec 2011 01:29:38 +0000 Subject: Add Pass(), which implements move semantics, to scoped_ptr, scoped_array, and scoped_ptr_malloc. This modification to the scopers implements the "moveable but not copyable" semantics that were introduced in C++11's unique_ptr<>. With this, is now possible to use scopers as an argument type or a return type. This signifies, in the type system, transfer of ownership into a function or out of a function respectively. Calling, or returning such a function MUST use the temporary resulting from the scoper's Pass() function. You CANNOT just pass the scoper by copy as there is still no copy constructor or assignment operator; trying to do so will yield a compilation error. This distinction makes it possible to avoid the implicit ownership transfer issues of auto_ptr, but still allow us to have compiler enforced ownership transfer. Also adds a Passed() helper that allows using a scoper with Bind(). BUG=96118 TEST=new unittests Review URL: http://codereview.chromium.org/8774032 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113722 0039d316-1c4b-4281-b951-d872f2087c98 --- base/memory/scoped_ptr.h | 93 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 12 deletions(-) (limited to 'base/memory/scoped_ptr.h') diff --git a/base/memory/scoped_ptr.h b/base/memory/scoped_ptr.h index 5ac6846..2d69a9d 100644 --- a/base/memory/scoped_ptr.h +++ b/base/memory/scoped_ptr.h @@ -32,6 +32,41 @@ // foo.get()->Method(); // Foo::Method on the 0th element. // foo[10].Method(); // Foo::Method on the 10th element. // } +// +// These scopers also implement part of the functionality of C++11 unique_ptr +// in that they are "movable but not copyable." You can use the scopers in +// the parameter and return types of functions to signify ownership transfer +// in to and out of a function. When calling a function that has a scoper +// as the argument type, it must be called with the result of an analogous +// scoper's Pass() function or another function that generates a temporary; +// passing by copy will NOT work. Here is an example using scoped_ptr: +// +// void TakesOwnership(scoped_ptr arg) { +// // Do something with arg +// } +// scoped_ptr CreateFoo() { +// // No need for calling Pass() because we are constructing a temporary +// // for the return value. +// return scoped_ptr(new Foo("new")); +// } +// scoped_ptr PassThru(scoped_ptr arg) { +// return arg.Pass(); +// } +// +// { +// scoped_ptr ptr(new Foo("yay")); // ptr manages Foo("yay)" +// TakesOwnership(ptr.Pass()); // ptr no longer owns Foo("yay"). +// scoped_ptr ptr2 = CreateFoo(); // ptr2 owns the return Foo. +// scoped_ptr ptr3 = // ptr3 now owns what was in ptr2. +// PassThru(ptr2.Pass()); // ptr2 is correspondingly NULL. +// } +// +// Notice that if you do not call Pass() when returning from PassThru(), or +// when invoking TakesOwnership(), the code will not compile because scopers +// are not copyable; they only implement move semantics which require calling +// the Pass() function to signify a destructive transfer of state. CreateFoo() +// is different though because we are constructing a temporary on the return +// line and thus can avoid needing to call Pass(). #ifndef BASE_MEMORY_SCOPED_PTR_H_ #define BASE_MEMORY_SCOPED_PTR_H_ @@ -47,12 +82,35 @@ #include "base/compiler_specific.h" +// Macro with the boilerplate C++03 move emulation for a class. +// +// In C++11, this is done via rvalue references. Here, we use C++03 move +// emulation to fake an rvalue reference. For a more thorough explanation +// of the technique, see: +// +// http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor +// +#define CPP_03_MOVE_EMULATION(scoper, field) \ + private: \ + struct RValue { \ + explicit RValue(scoper& obj) : obj_(obj) {} \ + scoper& obj_; \ + }; \ + public: \ + operator RValue() { return RValue(*this); } \ + scoper(RValue proxy) : field(proxy.obj_.release()) { } \ + scoper& operator=(RValue proxy) { \ + swap(proxy.obj_); \ + return *this; \ + } \ + scoper Pass() { return scoper(RValue(*this)); } + // A scoped_ptr is like a T*, except that the destructor of scoped_ptr // automatically deletes the pointer it holds (if any). // That is, scoped_ptr owns the T object that it points to. // Like a T*, a scoped_ptr may hold either NULL or a pointer to a T object. // Also like T*, scoped_ptr is thread-compatible, and once you -// dereference it, you get the threadsafety guarantees of T. +// dereference it, you get the thread safety guarantees of T. // // The size of a scoped_ptr is small: // sizeof(scoped_ptr) == sizeof(C*) @@ -122,6 +180,8 @@ class scoped_ptr { return retVal; } + CPP_03_MOVE_EMULATION(scoped_ptr, ptr_); + private: C* ptr_; @@ -131,9 +191,10 @@ class scoped_ptr { template bool operator==(scoped_ptr const& p2) const; template bool operator!=(scoped_ptr const& p2) const; - // Disallow evil constructors - scoped_ptr(const scoped_ptr&); - void operator=(const scoped_ptr&); + // Disallow evil constructors. Note that MUST NOT take a const& because we + // are implementing move semantics. See the CPP_03_MOVE_EMULATION macro. + scoped_ptr(scoped_ptr&); + void operator=(scoped_ptr&); }; // Free functions @@ -158,7 +219,7 @@ bool operator!=(C* p1, const scoped_ptr& p2) { // As with scoped_ptr, a scoped_array either points to an object // or is NULL. A scoped_array owns the object that it points to. // scoped_array is thread-compatible, and once you index into it, -// the returned objects have only the threadsafety guarantees of T. +// the returned objects have only the thread safety guarantees of T. // // Size: sizeof(scoped_array) == sizeof(C*) template @@ -168,7 +229,7 @@ class scoped_array { // The element type typedef C element_type; - // Constructor. Defaults to intializing with NULL. + // Constructor. Defaults to initializing with NULL. // There is no way to create an uninitialized scoped_array. // The input parameter must be allocated with new []. explicit scoped_array(C* p = NULL) : array_(p) { } @@ -229,6 +290,8 @@ class scoped_array { return retVal; } + CPP_03_MOVE_EMULATION(scoped_array, array_); + private: C* array_; @@ -236,9 +299,10 @@ class scoped_array { template bool operator==(scoped_array const& p2) const; template bool operator!=(scoped_array const& p2) const; - // Disallow evil constructors - scoped_array(const scoped_array&); - void operator=(const scoped_array&); + // Disallow evil constructors. Note that MUST NOT take a const& because we + // are implementing move semantics. See the CPP_03_MOVE_EMULATION macro. + scoped_array(scoped_array&); + void operator=(scoped_array&); }; // Free functions @@ -347,6 +411,8 @@ class scoped_ptr_malloc { return tmp; } + CPP_03_MOVE_EMULATION(scoped_ptr_malloc, ptr_); + private: C* ptr_; @@ -356,11 +422,14 @@ class scoped_ptr_malloc { template bool operator!=(scoped_ptr_malloc const& p) const; - // Disallow evil constructors - scoped_ptr_malloc(const scoped_ptr_malloc&); - void operator=(const scoped_ptr_malloc&); + // Disallow evil constructors. Note that MUST NOT take a const& because we + // are implementing move semantics. See the CPP_03_MOVE_EMULATION macro. + scoped_ptr_malloc(scoped_ptr_malloc&); + void operator=(scoped_ptr_malloc&); }; +#undef CPP_03_MOVE_EMULATION + template inline void swap(scoped_ptr_malloc& a, scoped_ptr_malloc& b) { a.swap(b); -- cgit v1.1