Commit 6a849971 authored by ajwong@chromium.org's avatar ajwong@chromium.org

Redo r113722 - Add Pass(), which implements move semantics, to scoped_ptr, scoped_array....

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 resultin

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

Original review URL: http://codereview.chromium.org/8774032

Review URL: http://codereview.chromium.org/8897005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113922 0039d316-1c4b-4281-b951-d872f2087c98
parent 20cea59e
...@@ -6,19 +6,29 @@ ...@@ -6,19 +6,29 @@
// can be used specify the refcounting and reference semantics of arguments // can be used specify the refcounting and reference semantics of arguments
// that are bound by the Bind() function in base/bind.h. // that are bound by the Bind() function in base/bind.h.
// //
// The public functions are base::Unretained(), base::Owned(), // The public functions are base::Unretained(), base::Owned(), bass::Passed(),
// base::ConstRef(), and base::IgnoreReturn(). // base::ConstRef(), and base::IgnoreResult().
// //
// Unretained() allows Bind() to bind a non-refcounted class, and to disable // Unretained() allows Bind() to bind a non-refcounted class, and to disable
// refcounting on arguments that are refcounted objects. // refcounting on arguments that are refcounted objects.
//
// Owned() transfers ownership of an object to the Callback resulting from // Owned() transfers ownership of an object to the Callback resulting from
// bind; the object will be deleted when the Callback is deleted. // bind; the object will be deleted when the Callback is deleted.
//
// Passed() is for transferring movable-but-not-copyable types (eg. scoped_ptr)
// through a Callback. Logically, this signifies a destructive transfer of
// the state of the argument into the target function. Invoking
// Callback::Run() twice on a Callback that was created with a Passed()
// argument will CHECK() because the first invocation would have already
// transferred ownership to the target function.
//
// ConstRef() allows binding a constant reference to an argument rather // ConstRef() allows binding a constant reference to an argument rather
// than a copy. // than a copy.
// IgnoreReturn() is used to adapt a 0-argument Callback with a return type to
// a Closure. This is useful if you need to PostTask with a function that has
// a return value that you don't care about.
// //
// IgnoreResult() is used to adapt a function or Callback with a return type to
// one with a void return. This is most useful if you have a function with,
// say, a pesky ignorable bool return that you want to use with PostTask or
// something else that expect a Callback with a void return.
// //
// EXAMPLE OF Unretained(): // EXAMPLE OF Unretained():
// //
...@@ -75,13 +85,45 @@ ...@@ -75,13 +85,45 @@
// its bound callbacks. // its bound callbacks.
// //
// //
// EXAMPLE OF IgnoreReturn(): // EXAMPLE OF IgnoreResult():
// //
// int DoSomething(int arg) { cout << arg << endl; } // int DoSomething(int arg) { cout << arg << endl; }
// Callback<int(void)> cb = Bind(&DoSomething, 1); //
// Closure c = IgnoreReturn(cb); // Prints "1" // // Assign to a Callback with a void return type.
// or // Callback<void(int)> cb = Bind(IgnoreResult(&DoSomething));
// ml->PostTask(FROM_HERE, IgnoreReturn(cb)); // Prints "1" on |ml| // cb->Run(1); // Prints "1".
//
// // Prints "1" on |ml|.
// ml->PostTask(FROM_HERE, Bind(IgnoreResult(&DoSomething), 1);
//
//
// EXAMPLE OF Passed():
//
// void TakesOwnership(scoped_ptr<Foo> arg) { }
// scoped_ptr<Foo> CreateFoo() { return scoped_ptr<Foo>(new Foo()); }
//
// scoped_ptr<Foo> f(new Foo());
//
// // |cb| is given ownership of Foo(). |f| is now NULL.
// // You can use f.Pass() in place of &f, but it's more verbose.
// Closure cb = Bind(&TakesOwnership, Passed(&f));
//
// // Run was never called so |cb| still owns Foo() and deletes
// // it on Reset().
// cb.Reset();
//
// // |cb| is given a new Foo created by CreateFoo().
// cb = Bind(&TakesOwnership, Passed(CreateFoo()));
//
// // |arg| in TakesOwnership() is given ownership of Foo(). |cb|
// // no longer owns Foo() and, if reset, would not delete Foo().
// cb.Run(); // Foo() is now transferred to |arg| and deleted.
// cb.Run(); // This CHECK()s since Foo() already been used once.
//
// Passed() is particularly useful with PostTask() when you are transferring
// ownership of an argument into a task, but don't necessarily know if the
// task will always be executed. This can happen if the task is cancellable
// or if it is posted to a MessageLoopProxy.
#ifndef BASE_BIND_HELPERS_H_ #ifndef BASE_BIND_HELPERS_H_
#define BASE_BIND_HELPERS_H_ #define BASE_BIND_HELPERS_H_
...@@ -287,6 +329,45 @@ class OwnedWrapper { ...@@ -287,6 +329,45 @@ class OwnedWrapper {
mutable T* ptr_; mutable T* ptr_;
}; };
// PassedWrapper is a copyable adapter for a scoper that ignores const.
//
// It is needed to get around the fact that Bind() takes a const reference to
// all its arguments. Because Bind() takes a const reference to avoid
// unnecessary copies, it is incompatible with movable-but-not-copyable
// types; doing a destructive "move" of the type into Bind() would violate
// the const correctness.
//
// This conundrum cannot be solved without either C++11 rvalue references or
// a O(2^n) blowup of Bind() templates to handle each combination of regular
// types and movable-but-not-copyable types. Thus we introduce a wrapper type
// that is copyable to transmit the correct type information down into
// BindState<>. Ignoring const in this type makes sense because it is only
// created when we are explicitly trying to do a destructive move.
//
// Two notes:
// 1) PassedWrapper supports any type that has a "Pass()" function.
// This is intentional. The whitelisting of which specific types we
// support is maintained by CallbackParamTraits<>.
// 2) is_valid_ is distinct from NULL because it is valid to bind a "NULL"
// scoper to a Callback and allow the Callback to execute once.
template <typename T>
class PassedWrapper {
public:
explicit PassedWrapper(T scoper) : is_valid_(true), scoper_(scoper.Pass()) {}
PassedWrapper(const PassedWrapper& other)
: is_valid_(other.is_valid_), scoper_(other.scoper_.Pass()) {
}
T Pass() const {
CHECK(is_valid_);
is_valid_ = false;
return scoper_.Pass();
}
private:
mutable bool is_valid_;
mutable T scoper_;
};
// Unwrap the stored parameters for the wrappers above. // Unwrap the stored parameters for the wrappers above.
template <typename T> template <typename T>
struct UnwrapTraits { struct UnwrapTraits {
...@@ -330,9 +411,17 @@ struct UnwrapTraits<OwnedWrapper<T> > { ...@@ -330,9 +411,17 @@ struct UnwrapTraits<OwnedWrapper<T> > {
} }
}; };
template <typename T>
struct UnwrapTraits<PassedWrapper<T> > {
typedef T ForwardType;
static T Unwrap(PassedWrapper<T>& o) {
return o.Pass();
}
};
// Utility for handling different refcounting semantics in the Bind() // Utility for handling different refcounting semantics in the Bind()
// function. // function.
template <bool, typename T> template <bool is_method, typename T>
struct MaybeRefcount; struct MaybeRefcount;
template <typename T> template <typename T>
...@@ -348,21 +437,15 @@ struct MaybeRefcount<false, T[n]> { ...@@ -348,21 +437,15 @@ struct MaybeRefcount<false, T[n]> {
}; };
template <typename T> template <typename T>
struct MaybeRefcount<true, T*> { struct MaybeRefcount<true, T> {
static void AddRef(T* o) { o->AddRef(); } static void AddRef(const T&) {}
static void Release(T* o) { o->Release(); } static void Release(const T&) {}
};
template <typename T>
struct MaybeRefcount<true, UnretainedWrapper<T> > {
static void AddRef(const UnretainedWrapper<T>&) {}
static void Release(const UnretainedWrapper<T>&) {}
}; };
template <typename T> template <typename T>
struct MaybeRefcount<true, OwnedWrapper<T> > { struct MaybeRefcount<true, T*> {
static void AddRef(const OwnedWrapper<T>&) {} static void AddRef(T* o) { o->AddRef(); }
static void Release(const OwnedWrapper<T>&) {} static void Release(T* o) { o->Release(); }
}; };
// No need to additionally AddRef() and Release() since we are storing a // No need to additionally AddRef() and Release() since we are storing a
...@@ -379,19 +462,13 @@ struct MaybeRefcount<true, const T*> { ...@@ -379,19 +462,13 @@ struct MaybeRefcount<true, const T*> {
static void Release(const T* o) { o->Release(); } static void Release(const T* o) { o->Release(); }
}; };
template <typename T>
struct MaybeRefcount<true, WeakPtr<T> > {
static void AddRef(const WeakPtr<T>&) {}
static void Release(const WeakPtr<T>&) {}
};
template <typename R> template <typename R>
void VoidReturnAdapter(Callback<R(void)> callback) { void VoidReturnAdapter(Callback<R(void)> callback) {
callback.Run(); callback.Run();
} }
// IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a // IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a
// method. It is unsed internally by Bind() to select the correct // method. It is used internally by Bind() to select the correct
// InvokeHelper that will no-op itself in the event the WeakPtr<> for // InvokeHelper that will no-op itself in the event the WeakPtr<> for
// the target object is invalidated. // the target object is invalidated.
// //
...@@ -422,6 +499,20 @@ static inline internal::OwnedWrapper<T> Owned(T* o) { ...@@ -422,6 +499,20 @@ static inline internal::OwnedWrapper<T> Owned(T* o) {
return internal::OwnedWrapper<T>(o); return internal::OwnedWrapper<T>(o);
} }
// We offer 2 syntaxes for calling Passed(). The first takes a temporary and
// is best suited for use with the return value of a function. The second
// takes a pointer to the scoper and is just syntactic sugar to avoid having
// to write Passed(scoper.Pass()).
template <typename T>
static inline internal::PassedWrapper<T> Passed(T scoper) {
return internal::PassedWrapper<T>(scoper.Pass());
}
template <typename T>
static inline internal::PassedWrapper<T> Passed(T* scoper) {
return internal::PassedWrapper<T>(scoper->Pass());
}
// -- DEPRECATED -- Use IgnoreResult instead.
template <typename R> template <typename R>
static inline Closure IgnoreReturn(Callback<R(void)> callback) { static inline Closure IgnoreReturn(Callback<R(void)> callback) {
return Bind(&internal::VoidReturnAdapter<R>, callback); return Bind(&internal::VoidReturnAdapter<R>, callback);
...@@ -438,7 +529,6 @@ IgnoreResult(const Callback<T>& data) { ...@@ -438,7 +529,6 @@ IgnoreResult(const Callback<T>& data) {
return internal::IgnoreResultHelper<Callback<T> >(data); return internal::IgnoreResultHelper<Callback<T> >(data);
} }
} // namespace base } // namespace base
#endif // BASE_BIND_HELPERS_H_ #endif // BASE_BIND_HELPERS_H_
This diff is collapsed.
...@@ -82,7 +82,6 @@ namespace internal { ...@@ -82,7 +82,6 @@ namespace internal {
// into the Bind() system, doing most of the type resolution. // into the Bind() system, doing most of the type resolution.
// There are ARITY BindState types. // There are ARITY BindState types.
// RunnableAdapter<> // RunnableAdapter<>
// //
// The RunnableAdapter<> templates provide a uniform interface for invoking // The RunnableAdapter<> templates provide a uniform interface for invoking
...@@ -121,7 +120,7 @@ class RunnableAdapter<R(*)($for ARG , [[A$(ARG)]])> { ...@@ -121,7 +120,7 @@ class RunnableAdapter<R(*)($for ARG , [[A$(ARG)]])> {
} }
R Run($for ARG , [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) { R Run($for ARG , [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) {
return function_($for ARG , [[a$(ARG)]]); return function_($for ARG , [[CallbackForward(a$(ARG))]]);
} }
private: private:
...@@ -143,7 +142,7 @@ $if ARITY > 0[[, ]] $for ARG , [[A$(ARG)]]); ...@@ -143,7 +142,7 @@ $if ARITY > 0[[, ]] $for ARG , [[A$(ARG)]]);
R Run(T* object[[]] R Run(T* object[[]]
$if ARITY > 0[[, ]] $for ARG, [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) { $if ARITY > 0[[, ]] $for ARG, [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) {
return (object->*method_)($for ARG , [[a$(ARG)]]); return (object->*method_)($for ARG , [[CallbackForward(a$(ARG))]]);
} }
private: private:
...@@ -165,7 +164,7 @@ $if ARITY > 0[[, ]] $for ARG , [[A$(ARG)]]); ...@@ -165,7 +164,7 @@ $if ARITY > 0[[, ]] $for ARG , [[A$(ARG)]]);
R Run(const T* object[[]] R Run(const T* object[[]]
$if ARITY > 0[[, ]] $for ARG, [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) { $if ARITY > 0[[, ]] $for ARG, [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) {
return (object->*method_)($for ARG , [[a$(ARG)]]); return (object->*method_)($for ARG , [[CallbackForward(a$(ARG))]]);
} }
private: private:
...@@ -291,7 +290,7 @@ struct InvokeHelper<false, ReturnType, Runnable, ...@@ -291,7 +290,7 @@ struct InvokeHelper<false, ReturnType, Runnable,
void($for ARG , [[A$(ARG)]])> { void($for ARG , [[A$(ARG)]])> {
static ReturnType MakeItSo(Runnable runnable[[]] static ReturnType MakeItSo(Runnable runnable[[]]
$if ARITY > 0[[, ]] $for ARG , [[A$(ARG) a$(ARG)]]) { $if ARITY > 0[[, ]] $for ARG , [[A$(ARG) a$(ARG)]]) {
return runnable.Run($for ARG , [[a$(ARG)]]); return runnable.Run($for ARG , [[CallbackForward(a$(ARG))]]);
} }
}; };
...@@ -301,7 +300,7 @@ struct InvokeHelper<false, void, Runnable, ...@@ -301,7 +300,7 @@ struct InvokeHelper<false, void, Runnable,
void($for ARG , [[A$(ARG)]])> { void($for ARG , [[A$(ARG)]])> {
static void MakeItSo(Runnable runnable[[]] static void MakeItSo(Runnable runnable[[]]
$if ARITY > 0[[, ]] $for ARG , [[A$(ARG) a$(ARG)]]) { $if ARITY > 0[[, ]] $for ARG , [[A$(ARG) a$(ARG)]]) {
runnable.Run($for ARG , [[a$(ARG)]]); runnable.Run($for ARG , [[CallbackForward(a$(ARG))]]);
} }
}; };
...@@ -316,7 +315,7 @@ $if ARITY > 0[[, ]] $for ARG , [[A$(ARG) a$(ARG)]]) { ...@@ -316,7 +315,7 @@ $if ARITY > 0[[, ]] $for ARG , [[A$(ARG) a$(ARG)]]) {
return; return;
} }
runnable.Run($for ARG , [[a$(ARG)]]); runnable.Run($for ARG , [[CallbackForward(a$(ARG))]]);
} }
}; };
...@@ -404,7 +403,7 @@ typename CallbackParamTraits<X$(UNBOUND_ARG)>::ForwardType x$(UNBOUND_ARG) ...@@ -404,7 +403,7 @@ typename CallbackParamTraits<X$(UNBOUND_ARG)>::ForwardType x$(UNBOUND_ARG)
]] ]]
)> )>
::MakeItSo(storage->runnable_ ::MakeItSo(storage->runnable_
$if ARITY > 0[[, ]] $for ARG , [[x$(ARG)]]); $if ARITY > 0[[, ]] $for ARG , [[CallbackForward(x$(ARG))]]);
} }
}; };
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -102,7 +105,7 @@ class CopyCounter { ...@@ -102,7 +105,7 @@ class CopyCounter {
(*copies_)++; (*copies_)++;
} }
// Probing for copies from coerscion. // Probing for copies from coercion.
CopyCounter(const DerivedCopyCounter& other) CopyCounter(const DerivedCopyCounter& other)
: copies_(other.copies_), : copies_(other.copies_),
assigns_(other.assigns_) { assigns_(other.assigns_) {
...@@ -149,6 +152,11 @@ class DeleteCounter { ...@@ -149,6 +152,11 @@ class DeleteCounter {
int* deletes_; int* deletes_;
}; };
template <typename T>
T PassThru(T scoper) {
return scoper.Pass();
}
// Some test functions that we can Bind to. // Some test functions that we can Bind to.
template <typename T> template <typename T>
T PolymorphicIdentity(T t) { T PolymorphicIdentity(T t) {
...@@ -641,8 +649,8 @@ TEST_F(BindTest, Owned) { ...@@ -641,8 +649,8 @@ TEST_F(BindTest, Owned) {
// return the same value. // return the same value.
Callback<DeleteCounter*(void)> no_capture_cb = Callback<DeleteCounter*(void)> no_capture_cb =
Bind(&PolymorphicIdentity<DeleteCounter*>, Owned(counter)); Bind(&PolymorphicIdentity<DeleteCounter*>, Owned(counter));
EXPECT_EQ(counter, no_capture_cb.Run()); ASSERT_EQ(counter, no_capture_cb.Run());
EXPECT_EQ(counter, no_capture_cb.Run()); ASSERT_EQ(counter, no_capture_cb.Run());
EXPECT_EQ(0, deletes); EXPECT_EQ(0, deletes);
no_capture_cb.Reset(); // This should trigger a delete. no_capture_cb.Reset(); // This should trigger a delete.
EXPECT_EQ(1, deletes); EXPECT_EQ(1, deletes);
...@@ -657,11 +665,60 @@ TEST_F(BindTest, Owned) { ...@@ -657,11 +665,60 @@ TEST_F(BindTest, Owned) {
EXPECT_EQ(1, deletes); EXPECT_EQ(1, deletes);
} }
// Passed() wrapper support.
// - Passed() can be constructed from a pointer to scoper.
// - Passed() can be constructed from a scoper rvalue.
// - Using Passed() gives Callback Ownership.
// - Ownership is transferred from Callback to callee on the first Run().
// - Callback supports unbound arguments.
TEST_F(BindTest, ScopedPtr) {
int deletes = 0;
// Tests the Passed() function's support for pointers.
scoped_ptr<DeleteCounter> ptr(new DeleteCounter(&deletes));
Callback<scoped_ptr<DeleteCounter>(void)> unused_callback =
Bind(&PassThru<scoped_ptr<DeleteCounter> >, Passed(&ptr));
EXPECT_FALSE(ptr.get());
EXPECT_EQ(0, deletes);
// If we never invoke the Callback, it retains ownership and deletes.
unused_callback.Reset();
EXPECT_EQ(1, deletes);
// Tests the Passed() function's support for rvalues.
deletes = 0;
DeleteCounter* counter = new DeleteCounter(&deletes);
Callback<scoped_ptr<DeleteCounter>(void)> callback =
Bind(&PassThru<scoped_ptr<DeleteCounter> >,
Passed(scoped_ptr<DeleteCounter>(counter)));
EXPECT_FALSE(ptr.get());
EXPECT_EQ(0, deletes);
// Check that ownership can be transferred back out.
scoped_ptr<DeleteCounter> result = callback.Run();
ASSERT_EQ(counter, result.get());
EXPECT_EQ(0, deletes);
// Resetting does not delete since ownership was transferred.
callback.Reset();
EXPECT_EQ(0, deletes);
// Ensure that we actually did get ownership.
result.reset();
EXPECT_EQ(1, deletes);
// Test unbound argument forwarding.
Callback<scoped_ptr<DeleteCounter>(scoped_ptr<DeleteCounter>)> cb_unbound =
Bind(&PassThru<scoped_ptr<DeleteCounter> >);
ptr.reset(new DeleteCounter(&deletes));
cb_unbound.Run(ptr.Pass());
}
// Argument Copy-constructor usage for non-reference parameters. // Argument Copy-constructor usage for non-reference parameters.
// - Bound arguments are only copied once. // - Bound arguments are only copied once.
// - Forwarded arguments are only copied once. // - Forwarded arguments are only copied once.
// - Forwarded arguments with coerscions are only copied twice (once for the // - Forwarded arguments with coercions are only copied twice (once for the
// coerscion, and one for the final dispatch). // coercion, and one for the final dispatch).
TEST_F(BindTest, ArgumentCopies) { TEST_F(BindTest, ArgumentCopies) {
int copies = 0; int copies = 0;
int assigns = 0; int assigns = 0;
......
...@@ -312,7 +312,7 @@ class Callback<R(A1)> : public internal::CallbackBase { ...@@ -312,7 +312,7 @@ class Callback<R(A1)> : public internal::CallbackBase {
PolymorphicInvoke f = PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
return f(bind_state_.get(), a1); return f(bind_state_.get(), internal::CallbackForward(a1));
} }
private: private:
...@@ -355,8 +355,8 @@ class Callback<R(A1, A2)> : public internal::CallbackBase { ...@@ -355,8 +355,8 @@ class Callback<R(A1, A2)> : public internal::CallbackBase {
PolymorphicInvoke f = PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
return f(bind_state_.get(), a1, return f(bind_state_.get(), internal::CallbackForward(a1),
a2); internal::CallbackForward(a2));
} }
private: private:
...@@ -401,9 +401,9 @@ class Callback<R(A1, A2, A3)> : public internal::CallbackBase { ...@@ -401,9 +401,9 @@ class Callback<R(A1, A2, A3)> : public internal::CallbackBase {
PolymorphicInvoke f = PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
return f(bind_state_.get(), a1, return f(bind_state_.get(), internal::CallbackForward(a1),
a2, internal::CallbackForward(a2),
a3); internal::CallbackForward(a3));
} }
private: private:
...@@ -450,10 +450,10 @@ class Callback<R(A1, A2, A3, A4)> : public internal::CallbackBase { ...@@ -450,10 +450,10 @@ class Callback<R(A1, A2, A3, A4)> : public internal::CallbackBase {
PolymorphicInvoke f = PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
return f(bind_state_.get(), a1, return f(bind_state_.get(), internal::CallbackForward(a1),
a2, internal::CallbackForward(a2),
a3, internal::CallbackForward(a3),
a4); internal::CallbackForward(a4));
} }
private: private:
...@@ -503,11 +503,11 @@ class Callback<R(A1, A2, A3, A4, A5)> : public internal::CallbackBase { ...@@ -503,11 +503,11 @@ class Callback<R(A1, A2, A3, A4, A5)> : public internal::CallbackBase {
PolymorphicInvoke f = PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
return f(bind_state_.get(), a1, return f(bind_state_.get(), internal::CallbackForward(a1),
a2, internal::CallbackForward(a2),
a3, internal::CallbackForward(a3),
a4, internal::CallbackForward(a4),
a5); internal::CallbackForward(a5));
} }
private: private:
...@@ -559,12 +559,12 @@ class Callback<R(A1, A2, A3, A4, A5, A6)> : public internal::CallbackBase { ...@@ -559,12 +559,12 @@ class Callback<R(A1, A2, A3, A4, A5, A6)> : public internal::CallbackBase {
PolymorphicInvoke f = PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
return f(bind_state_.get(), a1, return f(bind_state_.get(), internal::CallbackForward(a1),
a2, internal::CallbackForward(a2),
a3, internal::CallbackForward(a3),
a4, internal::CallbackForward(a4),
a5, internal::CallbackForward(a5),
a6); internal::CallbackForward(a6));
} }
private: private:
...@@ -618,13 +618,13 @@ class Callback<R(A1, A2, A3, A4, A5, A6, A7)> : public internal::CallbackBase { ...@@ -618,13 +618,13 @@ class Callback<R(A1, A2, A3, A4, A5, A6, A7)> : public internal::CallbackBase {
PolymorphicInvoke f = PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
return f(bind_state_.get(), a1, return f(bind_state_.get(), internal::CallbackForward(a1),
a2, internal::CallbackForward(a2),
a3, internal::CallbackForward(a3),
a4, internal::CallbackForward(a4),
a5, internal::CallbackForward(a5),
a6, internal::CallbackForward(a6),
a7); internal::CallbackForward(a7));
} }
private: private:
......
...@@ -291,7 +291,7 @@ class Callback<R($for ARG , [[A$(ARG)]])> : public internal::CallbackBase { ...@@ -291,7 +291,7 @@ class Callback<R($for ARG , [[A$(ARG)]])> : public internal::CallbackBase {
return f(bind_state_.get()[[]] return f(bind_state_.get()[[]]
$if ARITY != 0 [[, ]] $if ARITY != 0 [[, ]]
$for ARG , $for ARG ,
[[a$(ARG)]]); [[internal::CallbackForward(a$(ARG))]]);
} }
private: private:
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/base_export.h" #include "base/base_export.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
namespace base { namespace base {
namespace internal { namespace internal {
...@@ -130,6 +131,64 @@ struct CallbackParamTraits<T[]> { ...@@ -130,6 +131,64 @@ struct CallbackParamTraits<T[]> {
typedef const T* StorageType; typedef const T* StorageType;
}; };
// Parameter traits for movable-but-not-copyable scopers.
//
// Callback<>/Bind() understands movable-but-not-copyable semantics where
// the type cannot be copied but can still have its state destructively
// transferred (aka. moved) to another instance of the same type by calling a
// helper function. When used with Bind(), this signifies transferal of the
// object's state to the target function.
//
// For these types, the ForwardType must not be a const reference, or a
// reference. A const reference is inappropriate, and would break const
// correctness, because we are implementing a destructive move. A non-const
// reference cannot be used with temporaries which means the result of a
// function or a cast would not be usable with Callback<> or Bind().
//
// TODO(ajwong): We might be able to use SFINAE to search for the existence of
// a Pass() function in the type and avoid the whitelist in CallbackParamTraits
// and CallbackForward.
template <typename T>
struct CallbackParamTraits<scoped_ptr<T> > {
typedef scoped_ptr<T> ForwardType;
typedef scoped_ptr<T> StorageType;
};
template <typename T>
struct CallbackParamTraits<scoped_array<T> > {
typedef scoped_array<T> ForwardType;
typedef scoped_array<T> StorageType;
};
template <typename T>
struct CallbackParamTraits<scoped_ptr_malloc<T> > {
typedef scoped_ptr_malloc<T> ForwardType;
typedef scoped_ptr_malloc<T> StorageType;
};
// CallbackForward() is a very limited simulation of C++11's std::forward()
// used by the Callback/Bind system for a set of movable-but-not-copyable
// types. It is needed because forwarding a movable-but-not-copyable
// argument to another function requires us to invoke the proper move
// operator to create a rvalue version of the type. The supported types are
// whitelisted below as overloads of the CallbackForward() function. The
// default template compiles out to be a no-op.
//
// In C++11, std::forward would replace all uses of this function. However, it
// is impossible to implement a general std::forward with C++11 due to a lack
// of rvalue references.
template <typename T>
T& CallbackForward(T& t) { return t; }
template <typename T>
scoped_ptr<T> CallbackForward(scoped_ptr<T>& p) { return p.Pass(); }
template <typename T>
scoped_ptr<T> CallbackForward(scoped_array<T>& p) { return p.Pass(); }
template <typename T>
scoped_ptr<T> CallbackForward(scoped_ptr_malloc<T>& p) { return p.Pass(); }
} // namespace internal } // namespace internal
} // namespace base } // namespace base
......
...@@ -32,6 +32,41 @@ ...@@ -32,6 +32,41 @@
// foo.get()->Method(); // Foo::Method on the 0th element. // foo.get()->Method(); // Foo::Method on the 0th element.
// foo[10].Method(); // Foo::Method on the 10th 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_ #ifndef BASE_MEMORY_SCOPED_PTR_H_
#define BASE_MEMORY_SCOPED_PTR_H_ #define BASE_MEMORY_SCOPED_PTR_H_
...@@ -47,12 +82,35 @@ ...@@ -47,12 +82,35 @@
#include "base/compiler_specific.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)); }
// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T> // A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
// automatically deletes the pointer it holds (if any). // automatically deletes the pointer it holds (if any).
// That is, scoped_ptr<T> owns the T object that it points to. // 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. // 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 // 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: // The size of a scoped_ptr is small:
// sizeof(scoped_ptr<C>) == sizeof(C*) // sizeof(scoped_ptr<C>) == sizeof(C*)
...@@ -122,6 +180,8 @@ class scoped_ptr { ...@@ -122,6 +180,8 @@ class scoped_ptr {
return retVal; return retVal;
} }
CPP_03_MOVE_EMULATION(scoped_ptr, ptr_);
private: private:
C* ptr_; C* ptr_;
...@@ -131,9 +191,10 @@ class scoped_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;
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 // Disallow evil constructors. Note that MUST NOT take a const& because we
scoped_ptr(const scoped_ptr&); // are implementing move semantics. See the CPP_03_MOVE_EMULATION macro.
void operator=(const scoped_ptr&); scoped_ptr(scoped_ptr&);
void operator=(scoped_ptr&);
}; };
// Free functions // Free functions
...@@ -158,7 +219,7 @@ bool operator!=(C* p1, const scoped_ptr<C>& p2) { ...@@ -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 // 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. // 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, // 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*) // Size: sizeof(scoped_array<C>) == sizeof(C*)
template <class C> template <class C>
...@@ -168,7 +229,7 @@ class scoped_array { ...@@ -168,7 +229,7 @@ class scoped_array {
// The element type // The element type
typedef C 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. // There is no way to create an uninitialized scoped_array.
// The input parameter must be allocated with new []. // The input parameter must be allocated with new [].
explicit scoped_array(C* p = NULL) : array_(p) { } explicit scoped_array(C* p = NULL) : array_(p) { }
...@@ -229,6 +290,8 @@ class scoped_array { ...@@ -229,6 +290,8 @@ class scoped_array {
return retVal; return retVal;
} }
CPP_03_MOVE_EMULATION(scoped_array, array_);
private: private:
C* array_; C* array_;
...@@ -236,9 +299,10 @@ class scoped_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;
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 // Disallow evil constructors. Note that MUST NOT take a const& because we
scoped_array(const scoped_array&); // are implementing move semantics. See the CPP_03_MOVE_EMULATION macro.
void operator=(const scoped_array&); scoped_array(scoped_array&);
void operator=(scoped_array&);
}; };
// Free functions // Free functions
...@@ -347,6 +411,8 @@ class scoped_ptr_malloc { ...@@ -347,6 +411,8 @@ class scoped_ptr_malloc {
return tmp; return tmp;
} }
CPP_03_MOVE_EMULATION(scoped_ptr_malloc, ptr_);
private: private:
C* ptr_; C* ptr_;
...@@ -356,11 +422,14 @@ class scoped_ptr_malloc { ...@@ -356,11 +422,14 @@ class scoped_ptr_malloc {
template <class C2, class GP> template <class C2, class GP>
bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const; bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const;
// Disallow evil constructors // Disallow evil constructors. Note that MUST NOT take a const& because we
scoped_ptr_malloc(const scoped_ptr_malloc&); // are implementing move semantics. See the CPP_03_MOVE_EMULATION macro.
void operator=(const scoped_ptr_malloc&); scoped_ptr_malloc(scoped_ptr_malloc&);
void operator=(scoped_ptr_malloc&);
}; };
#undef CPP_03_MOVE_EMULATION
template<class C, class FP> inline template<class C, class FP> inline
void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) { void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) {
a.swap(b); a.swap(b);
......
...@@ -23,6 +23,19 @@ class ConDecLogger { ...@@ -23,6 +23,19 @@ class ConDecLogger {
DISALLOW_COPY_AND_ASSIGN(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 } // namespace
TEST(ScopedPtrTest, ScopedPtr) { TEST(ScopedPtrTest, ScopedPtr) {
...@@ -166,4 +179,83 @@ TEST(ScopedPtrTest, ScopedArray) { ...@@ -166,4 +179,83 @@ TEST(ScopedPtrTest, ScopedArray) {
EXPECT_EQ(0, constructed); 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 // TODO scoped_ptr_malloc
...@@ -74,6 +74,19 @@ class ExtensionManifestTest : public testing::Test { ...@@ -74,6 +74,19 @@ class ExtensionManifestTest : public testing::Test {
Manifest(DictionaryValue* manifest, const char* name) Manifest(DictionaryValue* manifest, const char* name)
: name_(name), manifest_(manifest) { : name_(name), manifest_(manifest) {
} }
Manifest(const Manifest& m) {
// C++98 requires the copy constructor for a type to be visiable if you
// take a const-ref of a temporary for that type. Since Manifest
// contains a scoped_ptr, its implicit copy constructor is declared
// Manifest(Manifest&) according to spec 12.8.5. This breaks the first
// requirement and thus you cannot use it with LoadAndExpectError() or
// LoadAndExpectSuccess() easily.
//
// To get around this spec pedantry, we declare the copy constructor
// explicitly. It will never get invoked.
NOTREACHED();
}
const std::string& name() const { return name_; } const std::string& name() const { return name_; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment