summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-20 00:03:45 +0000
committerajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-20 00:03:45 +0000
commit59e639aeb0da096e017d0786cfe852319152d74b (patch)
tree9506217ef37baa893494a4fce2cffe434d30cda6
parent1462329583a8219fb9adee6dc95e29c927e9e165 (diff)
downloadchromium_src-59e639aeb0da096e017d0786cfe852319152d74b.zip
chromium_src-59e639aeb0da096e017d0786cfe852319152d74b.tar.gz
chromium_src-59e639aeb0da096e017d0786cfe852319152d74b.tar.bz2
Transfer the C++03 move-only type emulation into base/move.h and also make ScopedVector move-only.
Also: * Add a lot of documentation explaining what this macro does. * Change the implementation of RValue so it cannot be instantiated. The change to always use RValue& makes for more efficent code in debug builds. Looking at the disassembly for a simple use case (calling a function with one parameter), it removes the creation of one temporary. BUG=96118 TEST=new unittests. exist code still compiles. Review URL: https://chromiumcodereview.appspot.com/9207021 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@118388 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/base.gypi1
-rw-r--r--base/memory/scoped_ptr.h82
-rw-r--r--base/memory/scoped_vector.h13
-rw-r--r--base/memory/scoped_vector_unittest.cc37
-rw-r--r--base/move.h211
5 files changed, 293 insertions, 51 deletions
diff --git a/base/base.gypi b/base/base.gypi
index b6cce3d..b971be8 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -200,6 +200,7 @@
'metrics/stats_counters.h',
'metrics/stats_table.cc',
'metrics/stats_table.h',
+ 'move.h',
'native_library.h',
'native_library_mac.mm',
'native_library_posix.cc',
diff --git a/base/memory/scoped_ptr.h b/base/memory/scoped_ptr.h
index b0cf0a1c..6d054b4 100644
--- a/base/memory/scoped_ptr.h
+++ b/base/memory/scoped_ptr.h
@@ -81,29 +81,7 @@
#include <stdlib.h>
#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)); }
+#include "base/move.h"
// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
// automatically deletes the pointer it holds (if any).
@@ -116,6 +94,8 @@
// sizeof(scoped_ptr<C>) == sizeof(C*)
template <class C>
class scoped_ptr {
+ MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue);
+
public:
// The element type
@@ -129,8 +109,10 @@ class scoped_ptr {
// Constructor. Allows construction from a scoped_ptr rvalue for a
// convertible type.
template <typename U>
- scoped_ptr(scoped_ptr<U> other) : ptr_(other.release()) {
- }
+ scoped_ptr(scoped_ptr<U> other) : ptr_(other.release()) { }
+
+ // Constructor. Move constructor for C++03 move emulation of this type.
+ scoped_ptr(RValue& other) : ptr_(other.release()) { }
// Destructor. If there is a C object, delete it.
// We don't need to test ptr_ == NULL because C++ does that for us.
@@ -147,6 +129,12 @@ class scoped_ptr {
return *this;
}
+ // operator=. Move operator= for C++03 move emulation of this type.
+ scoped_ptr& operator=(RValue& rhs) {
+ swap(rhs);
+ return *this;
+ }
+
// Reset. Deletes the current owned object, if any.
// Then takes ownership of a new object, if given.
// this->reset(this->get()) works.
@@ -194,8 +182,6 @@ class scoped_ptr {
return retVal;
}
- CPP_03_MOVE_EMULATION(scoped_ptr, ptr_);
-
private:
C* ptr_;
@@ -205,10 +191,6 @@ 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. 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
@@ -238,6 +220,8 @@ bool operator!=(C* p1, const scoped_ptr<C>& p2) {
// Size: sizeof(scoped_array<C>) == sizeof(C*)
template <class C>
class scoped_array {
+ MOVE_ONLY_TYPE_FOR_CPP_03(scoped_array, RValue);
+
public:
// The element type
@@ -248,6 +232,9 @@ class scoped_array {
// The input parameter must be allocated with new [].
explicit scoped_array(C* p = NULL) : array_(p) { }
+ // Constructor. Move constructor for C++03 move emulation of this type.
+ scoped_array(RValue& other) : array_(other.release()) { }
+
// Destructor. If there is a C object, delete it.
// We don't need to test ptr_ == NULL because C++ does that for us.
~scoped_array() {
@@ -255,6 +242,12 @@ class scoped_array {
delete[] array_;
}
+ // operator=. Move operator= for C++03 move emulation of this type.
+ scoped_array& operator=(RValue& rhs) {
+ swap(rhs);
+ return *this;
+ }
+
// Reset. Deletes the current owned object, if any.
// Then takes ownership of a new object, if given.
// this->reset(this->get()) works.
@@ -304,19 +297,12 @@ class scoped_array {
return retVal;
}
- CPP_03_MOVE_EMULATION(scoped_array, array_);
-
private:
C* array_;
// Forbid comparison of different scoped_array types.
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. 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
@@ -349,6 +335,8 @@ class ScopedPtrMallocFree {
template<class C, class FreeProc = ScopedPtrMallocFree>
class scoped_ptr_malloc {
+ MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr_malloc, RValue);
+
public:
// The element type
@@ -361,11 +349,20 @@ class scoped_ptr_malloc {
// realloc.
explicit scoped_ptr_malloc(C* p = NULL): ptr_(p) {}
+ // Constructor. Move constructor for C++03 move emulation of this type.
+ scoped_ptr_malloc(RValue& other) : ptr_(other.release()) { }
+
// Destructor. If there is a C object, call the Free functor.
~scoped_ptr_malloc() {
reset();
}
+ // operator=. Move operator= for C++03 move emulation of this type.
+ scoped_ptr_malloc& operator=(RValue& rhs) {
+ swap(rhs);
+ return *this;
+ }
+
// Reset. Calls the Free functor on the current owned object, if any.
// Then takes ownership of a new object, if given.
// this->reset(this->get()) works.
@@ -425,8 +422,6 @@ class scoped_ptr_malloc {
return tmp;
}
- CPP_03_MOVE_EMULATION(scoped_ptr_malloc, ptr_);
-
private:
C* ptr_;
@@ -435,15 +430,8 @@ class scoped_ptr_malloc {
bool operator==(scoped_ptr_malloc<C2, GP> const& p) const;
template <class C2, class GP>
bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const;
-
- // 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_vector.h b/base/memory/scoped_vector.h
index 832d9dd..a6d362e 100644
--- a/base/memory/scoped_vector.h
+++ b/base/memory/scoped_vector.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -9,12 +9,15 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/move.h"
#include "base/stl_util.h"
// ScopedVector wraps a vector deleting the elements from its
// destructor.
template <class T>
class ScopedVector {
+ MOVE_ONLY_TYPE_FOR_CPP_03(ScopedVector, RValue);
+
public:
typedef typename std::vector<T*>::iterator iterator;
typedef typename std::vector<T*>::const_iterator const_iterator;
@@ -24,6 +27,12 @@ class ScopedVector {
ScopedVector() {}
~ScopedVector() { reset(); }
+ ScopedVector(RValue& other) { swap(other); }
+
+ ScopedVector& operator=(RValue& rhs) {
+ swap(rhs);
+ return *this;
+ }
std::vector<T*>* operator->() { return &v; }
const std::vector<T*>* operator->() const { return &v; }
@@ -89,8 +98,6 @@ class ScopedVector {
}
private:
std::vector<T*> v;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedVector);
};
#endif // BASE_MEMORY_SCOPED_VECTOR_H_
diff --git a/base/memory/scoped_vector_unittest.cc b/base/memory/scoped_vector_unittest.cc
index d2f3d0a..cda1e4b 100644
--- a/base/memory/scoped_vector_unittest.cc
+++ b/base/memory/scoped_vector_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -126,6 +126,41 @@ TEST(ScopedVectorTest, Scope) {
EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state());
}
+TEST(ScopedVectorTest, MoveConstruct) {
+ LifeCycleWatcher watcher;
+ EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state());
+ {
+ ScopedVector<LifeCycleObject> scoped_vector;
+ scoped_vector.push_back(watcher.NewLifeCycleObject());
+ EXPECT_FALSE(scoped_vector.empty());
+
+ ScopedVector<LifeCycleObject> scoped_vector_copy(scoped_vector.Pass());
+ EXPECT_TRUE(scoped_vector.empty());
+ EXPECT_FALSE(scoped_vector_copy.empty());
+
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ }
+ EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state());
+}
+
+TEST(ScopedVectorTest, MoveAssign) {
+ LifeCycleWatcher watcher;
+ EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state());
+ {
+ ScopedVector<LifeCycleObject> scoped_vector;
+ scoped_vector.push_back(watcher.NewLifeCycleObject());
+ ScopedVector<LifeCycleObject> scoped_vector_assign;
+ EXPECT_FALSE(scoped_vector.empty());
+
+ scoped_vector_assign = scoped_vector.Pass();
+ EXPECT_TRUE(scoped_vector.empty());
+ EXPECT_FALSE(scoped_vector_assign.empty());
+
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ }
+ EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state());
+}
+
TEST(ScopedVectorTest, InsertRange) {
LifeCycleWatcher watchers[5];
diff --git a/base/move.h b/base/move.h
new file mode 100644
index 0000000..f9408f7
--- /dev/null
+++ b/base/move.h
@@ -0,0 +1,211 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MOVE_H_
+#define BASE_MOVE_H_
+
+// Macro with the boilerplate that makes a type move-only in C++03.
+//
+// USAGE
+//
+// This macro should be used instead of DISALLOW_COPY_AND_ASSIGN to create
+// a "move-only" type. Unlike DISALLOW_COPY_AND_ASSIGN, this macro should be
+// the first line in a class declaration.
+//
+// A class using this macro must call .Pass() (or somehow be an r-value already)
+// before it can be:
+//
+// * Passed as a function argument
+// * Used as the right-hand side of an assignment
+// * Return from a function
+//
+// Each class will still need to define their own "move constructor" and "move
+// operator=" to make this useful. Here's an example of the macro, the move
+// constructor, and the move operator= from the scoped_ptr class:
+//
+// template <typename T>
+// class scoped_ptr {
+// MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)
+// public:
+// scoped_ptr(RValue& other) : ptr_(other.release()) { }
+// scoped_ptr& operator=(RValue& other) {
+// swap(other);
+// return *this;
+// }
+// };
+//
+// Note that the constructor must NOT be marked explicit.
+//
+// For consistency, the second parameter to the macro should always be RValue
+// unless you have a strong reason to do otherwise. It is only exposed as a
+// macro parameter so that the move constructor and move operator= don't look
+// like they're using a phantom type.
+//
+//
+// HOW THIS WORKS
+//
+// For a thorough explanation of this technique, see:
+//
+// http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor
+//
+// The summary is that we take advantage of 2 properties:
+//
+// 1) non-const references will not bind to r-values.
+// 2) C++ can apply one user-defined conversion when initializing a
+// variable.
+//
+// The first lets us disable the copy constructor and assignment operator
+// by declaring private version of them with a non-const reference parameter.
+//
+// For l-values, direct initialization still fails like in
+// DISALLOW_COPY_AND_ASSIGN because the copy constructor and assignment
+// operators are private.
+//
+// For r-values, the situation is different. The copy constructor and
+// assignment operator are not viable due to (1), so we are trying to call
+// a non-existent constructor and non-existing operator= rather than a private
+// one. Since we have not committed an error quite yet, we can provide an
+// alternate conversion sequence and a constructor. We add
+//
+// * a private struct named "RValue"
+// * a user-defined conversion "operator RValue&()"
+// * a "move constructor" and "move operator=" that take the RValue& as
+// their sole parameter.
+//
+// Only r-values will trigger this sequence and execute our "move constructor"
+// or "move operator=." L-values will match the private copy constructor and
+// operator= first giving a "private in this context" error. This combination
+// gives us a move-only type.
+//
+// For signaling a destructive transfer of data from an l-value, we provide a
+// method named Pass() which creates an r-value for the current instance
+// triggering the move constructor or move operator=.
+//
+// Other ways to get r-values is to use the result of an expression like a
+// function call.
+//
+// Here's an example with comments explaining what gets triggered where:
+//
+// class Foo {
+// MOVE_ONLY_TYPE_FOR_CPP_03(Foo, RValue);
+//
+// public:
+// ... API ...
+// Foo(RValue& other); // Move constructor.
+// Foo& operator=(RValue& rhs); // Move operator=
+// };
+//
+// Foo MakeFoo(); // Function that returns a Foo.
+//
+// Foo f;
+// Foo f_copy(f); // ERROR: Foo(Foo&) is private in this context.
+// Foo f_assign;
+// f_assign = f; // ERROR: operator=(Foo&) is private in this context.
+//
+//
+// Foo f(MakeFoo()); // R-value so alternate conversion executed.
+// Foo f_copy(f.Pass()); // R-value so alternate conversion executed.
+// f = f_copy.Pass(); // R-value so alternate conversion executed.
+//
+//
+// IMPLEMENTATION SUBTLETIES WITH RValue
+//
+// The RValue struct has subtle properties:
+//
+// 1) All its methods are declared, but intentionally not defined.
+// 2) It is *never* instantiated.
+// 3) It is a child of the move-only type.
+//
+// (1) is a guard against accidental violation of (2). If an instance of
+// RValue were ever created, either as a temporary, or as a copy to some
+// function parameter or field of a class, the binary will not link.
+//
+// This ensures that RValue can only exist as a temporary which is important
+// to avoid accidental danging references.
+//
+// (3) allows us to get around instantiations because our user-defined
+// conversion can return a downcast of this pointer.
+//
+// operator RValue&() { return *reinterpret_cast<RValue*>(this); }
+//
+// Because RValue does not extend the object size or add any virtual methods,
+// this type-pun is safe.
+//
+// An alternative implementation would be to make RValue into a concrete
+// struct that holds a reference to the type. But in the non-optimized build,
+// this causes unnecessary temporaries to be made bloating the object files.
+// Also, it would then be possible to accidentally persist an RValue instance.
+//
+//
+// COMPARED TO C++11
+//
+// In C++11, you would implement this functionality using an r-value reference
+// and our .Pass() method would be replaced with a call to std::move().
+//
+// This emulation also has a deficiency where it uses up the single
+// user-defined conversion allowed by C++ during initialization. This can
+// cause problems in some API edge cases. For instance, in scoped_ptr, it is
+// impossible to make an function "void Foo(scoped_ptr<Parent> p)" accept a
+// value of type scoped_ptr<Child> even if you add a constructor to
+// scoped_ptr<> that would make it look like it should work. C++11 does not
+// have this deficiency.
+//
+//
+// COMPARED TO Boost.Move
+//
+// Our implementation is based on Boost.Move, but we keep the RValue struct
+// private to the move-only type.
+//
+// In Boost.Move, RValue is the boost::rv<> template. This type can be used
+// when writing APIs like:
+//
+// void MyFunc(boost::rv<Foo>& f)
+//
+// that can take advantage of rv<> to avoid extra copies of a type. However you
+// would still be able to call this version of MyFunc with an l-value:
+//
+// Foo f;
+// MyFunc(f); // Uh oh, we probably just destroyed |f| w/o calling Pass().
+//
+// unless someone is very careful to also declare a parallel override like:
+//
+// void MyFunc(const Foo& f)
+//
+// that would catch the l-values first. This was declared unsafe in C++11 and
+// a C++11 compiler will explicitly fail MyFunc(f). Unfortunately, we cannot
+// ensure this in C++03.
+//
+// Since we have no need for writing such APIs yet, our implementation keeps
+// RValue private and uses a .Pass() method to do the conversion instead of
+// trying to write a version of "std::move()." Writing an API like std::move()
+// would require the RValue structs to be public.
+//
+//
+// CAVEATS
+//
+// If you include a move-only type as a field inside a class that does not
+// explicitly declare a copy constructor, the containing class's implicit
+// copy constructor will change from Containing(const Containing&) to
+// Containing(Containing&). This can cause some unexpected errors.
+//
+// http://llvm.org/bugs/show_bug.cgi?id=11528
+//
+// The workaround is to explicitly declare your copy constructor.
+//
+#define MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \
+ private: \
+ struct rvalue_type : public type { \
+ rvalue_type(); \
+ ~rvalue_type(); \
+ rvalue_type(const rvalue_type&); \
+ void operator=(const rvalue_type&); \
+ }; \
+ type(type&); \
+ void operator=(type&); \
+ public: \
+ operator rvalue_type&() { return *reinterpret_cast<rvalue_type*>(this); } \
+ type Pass() { return type(*reinterpret_cast<rvalue_type*>(this)); } \
+ private:
+
+#endif // BASE_MOVE_H_