diff options
author | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-22 21:12:58 +0000 |
---|---|---|
committer | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-22 21:12:58 +0000 |
commit | 206a2ae8a1ebd2b040753fff7da61bbca117757f (patch) | |
tree | 8ca9b401ccb608dada5e388d0b7905ed2e140fe1 /base/memory | |
parent | b06392858ab76a6720352022f224b1144af06548 (diff) | |
download | chromium_src-206a2ae8a1ebd2b040753fff7da61bbca117757f.zip chromium_src-206a2ae8a1ebd2b040753fff7da61bbca117757f.tar.gz chromium_src-206a2ae8a1ebd2b040753fff7da61bbca117757f.tar.bz2 |
Redo r113722 - Add Pass(), which implements move semantics, to scoped_ptr, scoped_array....
-- This time for sure. --
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 a function or explicit cast.
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().
Original Review URL: http://codereview.chromium.org/8774032
BUG=96118
TEST=new unittests
Review URL: http://codereview.chromium.org/9018037
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@115607 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/memory')
-rw-r--r-- | base/memory/scoped_ptr.h | 93 | ||||
-rw-r--r-- | base/memory/scoped_ptr_unittest.cc | 92 |
2 files changed, 173 insertions, 12 deletions
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<Foo> arg) { +// // Do something with arg +// } +// scoped_ptr<Foo> CreateFoo() { +// // No need for calling Pass() because we are constructing a temporary +// // for the return value. +// return scoped_ptr<Foo>(new Foo("new")); +// } +// scoped_ptr<Foo> PassThru(scoped_ptr<Foo> arg) { +// return arg.Pass(); +// } +// +// { +// scoped_ptr<Foo> ptr(new Foo("yay")); // ptr manages Foo("yay)" +// TakesOwnership(ptr.Pass()); // ptr no longer owns Foo("yay"). +// scoped_ptr<Foo> ptr2 = CreateFoo(); // ptr2 owns the return Foo. +// scoped_ptr<Foo> 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<T> is like a T*, except that the destructor of scoped_ptr<T> // automatically deletes the pointer it holds (if any). // That is, scoped_ptr<T> owns the T object that it points to. // Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object. // Also like T*, scoped_ptr<T> 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<C>) == 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 <class C2> bool operator==(scoped_ptr<C2> const& p2) const; template <class C2> bool operator!=(scoped_ptr<C2> 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<C>& p2) { // As with scoped_ptr<C>, a scoped_array<C> either points to an object // or is NULL. A scoped_array<C> owns the object that it points to. // scoped_array<T> 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<C>) == sizeof(C*) template <class C> @@ -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 <class C2> bool operator==(scoped_array<C2> const& p2) const; template <class C2> bool operator!=(scoped_array<C2> 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 <class C2, class GP> bool operator!=(scoped_ptr_malloc<C2, GP> 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<class C, class FP> inline void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) { a.swap(b); diff --git a/base/memory/scoped_ptr_unittest.cc b/base/memory/scoped_ptr_unittest.cc index 7519051..9a37cbf 100644 --- a/base/memory/scoped_ptr_unittest.cc +++ b/base/memory/scoped_ptr_unittest.cc @@ -23,6 +23,19 @@ class ConDecLogger { DISALLOW_COPY_AND_ASSIGN(ConDecLogger); }; +scoped_ptr<ConDecLogger> PassThru(scoped_ptr<ConDecLogger> logger) { + return logger.Pass(); +} + +void GrabAndDrop(scoped_ptr<ConDecLogger> logger) { +} + +// Do not delete this function! It's existence is to test that you can +// return a temporarily constructed version of the scoper. +scoped_ptr<ConDecLogger> TestReturnOfType(int* constructed) { + return scoped_ptr<ConDecLogger>(new ConDecLogger(constructed)); +} + } // namespace TEST(ScopedPtrTest, ScopedPtr) { @@ -166,4 +179,83 @@ TEST(ScopedPtrTest, ScopedArray) { EXPECT_EQ(0, constructed); } +TEST(ScopedPtrTest, PassBehavior) { + int constructed = 0; + { + ConDecLogger* logger = new ConDecLogger(&constructed); + scoped_ptr<ConDecLogger> scoper(logger); + EXPECT_EQ(1, constructed); + + // Test Pass() with constructor; + scoped_ptr<ConDecLogger> scoper2(scoper.Pass()); + EXPECT_EQ(1, constructed); + + // Test Pass() with assignment; + scoped_ptr<ConDecLogger> scoper3; + scoper3 = scoper2.Pass(); + EXPECT_EQ(1, constructed); + EXPECT_FALSE(scoper.get()); + EXPECT_FALSE(scoper2.get()); + EXPECT_TRUE(scoper3.get()); + } + + // Test uncaught Pass() does not leak. + { + ConDecLogger* logger = new ConDecLogger(&constructed); + scoped_ptr<ConDecLogger> scoper(logger); + EXPECT_EQ(1, constructed); + + // Should auto-destruct logger by end of scope. + scoper.Pass(); + EXPECT_FALSE(scoper.get()); + } + EXPECT_EQ(0, constructed); + + // Test that passing to function which does nothing does not leak. + { + ConDecLogger* logger = new ConDecLogger(&constructed); + scoped_ptr<ConDecLogger> scoper(logger); + EXPECT_EQ(1, constructed); + + // Should auto-destruct logger by end of scope. + GrabAndDrop(scoper.Pass()); + EXPECT_FALSE(scoper.get()); + } + EXPECT_EQ(0, constructed); +} + +TEST(ScopedPtrTest, ReturnTypeBehavior) { + int constructed = 0; + + // Test that we can return a scoped_ptr. + { + ConDecLogger* logger = new ConDecLogger(&constructed); + scoped_ptr<ConDecLogger> scoper(logger); + EXPECT_EQ(1, constructed); + + PassThru(scoper.Pass()); + EXPECT_FALSE(scoper.get()); + } + EXPECT_EQ(0, constructed); + + // Test uncaught return type not leak. + { + ConDecLogger* logger = new ConDecLogger(&constructed); + scoped_ptr<ConDecLogger> scoper(logger); + EXPECT_EQ(1, constructed); + + // Should auto-destruct logger by end of scope. + PassThru(scoper.Pass()); + EXPECT_FALSE(scoper.get()); + } + EXPECT_EQ(0, constructed); + + // Call TestReturnOfType() so the compiler doesn't warn for an unused + // function. + { + TestReturnOfType(&constructed); + } + EXPECT_EQ(0, constructed); +} + // TODO scoped_ptr_malloc |