diff options
author | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-18 04:05:14 +0000 |
---|---|---|
committer | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-18 04:05:14 +0000 |
commit | 15fcb6590c658858007a61dd24055dcefd0b699d (patch) | |
tree | 817040b4ccd84a59dcd778696ff328016083d95b | |
parent | 3d049916edeb079db28283fb230b89def0721547 (diff) | |
download | chromium_src-15fcb6590c658858007a61dd24055dcefd0b699d.zip chromium_src-15fcb6590c658858007a61dd24055dcefd0b699d.tar.gz chromium_src-15fcb6590c658858007a61dd24055dcefd0b699d.tar.bz2 |
Emptiness, Reset, and Comparison API for Callbacks.
Since Callback<> is essentially a smartpointer, some introspective APIs are
required for sensible usage.
BUG=35223
TEST=new unittests
Review URL: http://codereview.chromium.org/6507029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@75360 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/bind_unittest.cc | 12 | ||||
-rw-r--r-- | base/callback.h | 202 | ||||
-rw-r--r-- | base/callback.h.pump | 70 | ||||
-rw-r--r-- | base/callback_unittest.cc | 96 |
4 files changed, 268 insertions, 112 deletions
diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc index 47d971a..77eb98a 100644 --- a/base/bind_unittest.cc +++ b/base/bind_unittest.cc @@ -212,18 +212,6 @@ class BindTest : public ::testing::Test { StrictMock<NoRef>* BindTest::static_func_mock_ptr; -// Ensure we can create unbound callbacks. We need this to be able to store -// them in class members that can be initialized later. -TEST_F(BindTest, DefaultConstruction) { - Callback<void(void)> c0; - Callback<void(int)> c1; - Callback<void(int,int)> c2; - Callback<void(int,int,int)> c3; - Callback<void(int,int,int,int)> c4; - Callback<void(int,int,int,int,int)> c5; - Callback<void(int,int,int,int,int,int)> c6; -} - // Sanity check that we can instantiate a callback for each arity. TEST_F(BindTest, ArityTest) { Callback<int(void)> c0 = Bind(&Sum, 32, 16, 8, 4, 2, 1); diff --git a/base/callback.h b/base/callback.h index 05a7182..9e43afb 100644 --- a/base/callback.h +++ b/base/callback.h @@ -212,6 +212,49 @@ namespace base { +namespace internal { + +// Holds the methods that don't require specialization to reduce template bloat. +class CallbackBase { + public: + // Returns true if Callback is null (doesn't refer to anything). + bool is_null() const { + return invoker_storage_.get() == NULL; + } + + // Returns the Callback into an uninitalized state. + void Reset() { + invoker_storage_ = NULL; + polymorphic_invoke_ = NULL; + } + + bool Equals(const CallbackBase& other) const { + return invoker_storage_.get() == other.invoker_storage_.get() && + polymorphic_invoke_ == other.polymorphic_invoke_; + } + + protected: + // In C++, it is safe to cast function pointers to function pointers of + // another type. It is not okay to use void*. We create a InvokeFuncStorage + // that that can store our function pointer, and then cast it back to + // the original type on usage. + typedef void(*InvokeFuncStorage)(void); + + CallbackBase(InvokeFuncStorage polymorphic_invoke, + scoped_refptr<InvokerStorageBase>* invoker_storage) + : polymorphic_invoke_(polymorphic_invoke) { + if (invoker_storage) { + invoker_storage_.swap(*invoker_storage); + } + } + + scoped_refptr<InvokerStorageBase> invoker_storage_; + InvokeFuncStorage polymorphic_invoke_; +}; + +} // namespace internal + + // First, we forward declare the Callback class template. This informs the // compiler that the template only has 1 type parameter which is the function // signature that the Callback is representing. @@ -221,13 +264,12 @@ namespace base { // only has one type: the function signature. template <typename Sig> class Callback; - template <typename R> -class Callback<R(void)> { +class Callback<R(void)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -238,25 +280,25 @@ class Callback<R(void)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::FunctionTraits::DoInvoke), + &invoker_holder.invoker_storage_) { } - R Run(void) const { - return polymorphic_invoke_(invoker_storage_.get()); - } + R Run() const { + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; + return f(invoker_storage_.get()); + } }; template <typename R, typename A1> -class Callback<R(A1)> { +class Callback<R(A1)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -267,26 +309,26 @@ class Callback<R(A1)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::FunctionTraits::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1) const { - return polymorphic_invoke_(invoker_storage_.get(), a1); - } + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; + return f(invoker_storage_.get(), a1); + } }; template <typename R, typename A1, typename A2> -class Callback<R(A1, A2)> { +class Callback<R(A1, A2)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, const A2&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -297,29 +339,29 @@ class Callback<R(A1, A2)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::FunctionTraits::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1, const A2& a2) const { - return polymorphic_invoke_(invoker_storage_.get(), a1, - a2); - } + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; + return f(invoker_storage_.get(), a1, + a2); + } }; template <typename R, typename A1, typename A2, typename A3> -class Callback<R(A1, A2, A3)> { +class Callback<R(A1, A2, A3)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, const A2&, const A3&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -330,32 +372,32 @@ class Callback<R(A1, A2, A3)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::FunctionTraits::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1, const A2& a2, const A3& a3) const { - return polymorphic_invoke_(invoker_storage_.get(), a1, - a2, - a3); - } + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; + return f(invoker_storage_.get(), a1, + a2, + a3); + } }; template <typename R, typename A1, typename A2, typename A3, typename A4> -class Callback<R(A1, A2, A3, A4)> { +class Callback<R(A1, A2, A3, A4)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, const A2&, const A3&, const A4&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -366,28 +408,28 @@ class Callback<R(A1, A2, A3, A4)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::FunctionTraits::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1, const A2& a2, const A3& a3, const A4& a4) const { - return polymorphic_invoke_(invoker_storage_.get(), a1, - a2, - a3, - a4); - } + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; + return f(invoker_storage_.get(), a1, + a2, + a3, + a4); + } }; template <typename R, typename A1, typename A2, typename A3, typename A4, typename A5> -class Callback<R(A1, A2, A3, A4, A5)> { +class Callback<R(A1, A2, A3, A4, A5)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, const A2&, @@ -395,7 +437,7 @@ class Callback<R(A1, A2, A3, A4, A5)> { const A4&, const A5&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -406,8 +448,9 @@ class Callback<R(A1, A2, A3, A4, A5)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::FunctionTraits::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1, @@ -415,21 +458,20 @@ class Callback<R(A1, A2, A3, A4, A5)> { const A3& a3, const A4& a4, const A5& a5) const { - return polymorphic_invoke_(invoker_storage_.get(), a1, - a2, - a3, - a4, - a5); + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); + + return f(invoker_storage_.get(), a1, + a2, + a3, + a4, + a5); } - - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; }; template <typename R, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> -class Callback<R(A1, A2, A3, A4, A5, A6)> { +class Callback<R(A1, A2, A3, A4, A5, A6)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, const A2&, @@ -438,7 +480,7 @@ class Callback<R(A1, A2, A3, A4, A5, A6)> { const A5&, const A6&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -449,8 +491,9 @@ class Callback<R(A1, A2, A3, A4, A5, A6)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::FunctionTraits::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1, @@ -459,17 +502,16 @@ class Callback<R(A1, A2, A3, A4, A5, A6)> { const A4& a4, const A5& a5, const A6& a6) const { - return polymorphic_invoke_(invoker_storage_.get(), a1, - a2, - a3, - a4, - a5, - a6); + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); + + return f(invoker_storage_.get(), a1, + a2, + a3, + a4, + a5, + a6); } - - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; }; diff --git a/base/callback.h.pump b/base/callback.h.pump index 9fc4b0b..19b7987 100644 --- a/base/callback.h.pump +++ b/base/callback.h.pump @@ -216,6 +216,49 @@ $var MAX_ARITY = 6 namespace base { +namespace internal { + +// Holds the methods that don't require specialization to reduce template bloat. +class CallbackBase { + public: + // Returns true if Callback is null (doesn't refer to anything). + bool is_null() const { + return invoker_storage_.get() == NULL; + } + + // Returns the Callback into an uninitalized state. + void Reset() { + invoker_storage_ = NULL; + polymorphic_invoke_ = NULL; + } + + bool Equals(const CallbackBase& other) const { + return invoker_storage_.get() == other.invoker_storage_.get() && + polymorphic_invoke_ == other.polymorphic_invoke_; + } + + protected: + // In C++, it is safe to cast function pointers to function pointers of + // another type. It is not okay to use void*. We create a InvokeFuncStorage + // that that can store our function pointer, and then cast it back to + // the original type on usage. + typedef void(*InvokeFuncStorage)(void); + + CallbackBase(InvokeFuncStorage polymorphic_invoke, + scoped_refptr<InvokerStorageBase>* invoker_storage) + : polymorphic_invoke_(polymorphic_invoke) { + if (invoker_storage) { + invoker_storage_.swap(*invoker_storage); + } + } + + scoped_refptr<InvokerStorageBase> invoker_storage_; + InvokeFuncStorage polymorphic_invoke_; +}; + +} // namespace internal + + // First, we forward declare the Callback class template. This informs the // compiler that the template only has 1 type parameter which is the function // signature that the Callback is representing. @@ -226,17 +269,16 @@ namespace base { template <typename Sig> class Callback; - $range ARITY 0..MAX_ARITY $for ARITY [[ $range ARG 1..ARITY $if ARITY == 0 [[ template <typename R> -class Callback<R(void)> { +class Callback<R(void)> : public internal::CallbackBase { ]] $else [[ template <typename R, $for ARG , [[typename A$(ARG)]]> -class Callback<R($for ARG , [[A$(ARG)]])> { +class Callback<R($for ARG , [[A$(ARG)]])> : public internal::CallbackBase { ]] public: @@ -245,7 +287,7 @@ $if ARITY != 0 [[, ]] $for ARG , [[const A$(ARG)&]]); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -256,27 +298,21 @@ $for ARG , // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::FunctionTraits::DoInvoke), + &invoker_holder.invoker_storage_) { } - -$if ARITY == 0 [[ - R Run(void) const { -]] $else [[ R Run($for ARG , [[const A$(ARG)& a$(ARG)]]) const { -]] + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - return polymorphic_invoke_(invoker_storage_.get()[[]] + return f(invoker_storage_.get()[[]] $if ARITY != 0 [[, ]] $for ARG , - [[a$(ARG)]]); + [[a$(ARG)]]); } - - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; }; diff --git a/base/callback_unittest.cc b/base/callback_unittest.cc index bc15927..bf2b606 100644 --- a/base/callback_unittest.cc +++ b/base/callback_unittest.cc @@ -3,10 +3,12 @@ // found in the LICENSE file. #include "base/callback.h" +#include "base/callback_helpers.h" #include "base/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest.h" +namespace base { namespace { class HelperObject { @@ -19,9 +21,24 @@ class HelperObject { int next_number_; }; -} // namespace +struct FakeTraits { + static void DoInvoke(internal::InvokerStorageBase*) { + } +}; + +// White-box testpoints to inject into a Callback<> object for checking +// comparators and emptiness APIs. +class FakeInvokerStorage1 : public internal::InvokerStorageBase { + public: + typedef FakeTraits FunctionTraits; +}; + +class FakeInvokerStorage2 : public internal::InvokerStorageBase { + public: + typedef FakeTraits FunctionTraits; +}; -TEST(Callback, OneArg) { +TEST(CallbackOld, OneArg) { HelperObject obj; scoped_ptr<Callback1<int*>::Type> callback( NewCallback(&obj, &HelperObject::GetNextNumberArg)); @@ -31,10 +48,83 @@ TEST(Callback, OneArg) { EXPECT_EQ(number, 1); } -TEST(Callback, ReturnValue) { +TEST(CallbackOld, ReturnValue) { HelperObject obj; scoped_ptr<CallbackWithReturnValue<int>::Type> callback( NewCallbackWithReturnValue(&obj, &HelperObject::GetNextNumber)); EXPECT_EQ(callback->Run(), 1); } + +class CallbackTest : public ::testing::Test { + public: + CallbackTest() + : callback_a_(MakeInvokerStorageHolder(new FakeInvokerStorage1())), + callback_b_(MakeInvokerStorageHolder(new FakeInvokerStorage2())) { + } + + virtual ~CallbackTest() { + } + + protected: + Callback<void(void)> callback_a_; + const Callback<void(void)> callback_b_; // Ensure APIs work with const. + Callback<void(void)> null_callback_; +}; + +// Ensure we can create unbound callbacks. We need this to be able to store +// them in class members that can be initialized later. +TEST_F(CallbackTest, DefaultConstruction) { + Callback<void(void)> c0; + Callback<void(int)> c1; + Callback<void(int,int)> c2; + Callback<void(int,int,int)> c3; + Callback<void(int,int,int,int)> c4; + Callback<void(int,int,int,int,int)> c5; + Callback<void(int,int,int,int,int,int)> c6; + + EXPECT_TRUE(c0.is_null()); + EXPECT_TRUE(c1.is_null()); + EXPECT_TRUE(c2.is_null()); + EXPECT_TRUE(c3.is_null()); + EXPECT_TRUE(c4.is_null()); + EXPECT_TRUE(c5.is_null()); + EXPECT_TRUE(c6.is_null()); +} + +TEST_F(CallbackTest, IsNull) { + EXPECT_TRUE(null_callback_.is_null()); + EXPECT_FALSE(callback_a_.is_null()); + EXPECT_FALSE(callback_b_.is_null()); +} + +TEST_F(CallbackTest, Equals) { + EXPECT_TRUE(callback_a_.Equals(callback_a_)); + EXPECT_FALSE(callback_a_.Equals(callback_b_)); + EXPECT_FALSE(callback_b_.Equals(callback_a_)); + + // We should compare based on instance, not type. + Callback<void(void)> callback_c( + MakeInvokerStorageHolder(new FakeInvokerStorage1())); + Callback<void(void)> callback_a2 = callback_a_; + EXPECT_TRUE(callback_a_.Equals(callback_a2)); + EXPECT_FALSE(callback_a_.Equals(callback_c)); + + // Empty, however, is always equal to empty. + Callback<void(void)> empty2; + EXPECT_TRUE(null_callback_.Equals(empty2)); +} + +TEST_F(CallbackTest, Reset) { + // Resetting should bring us back to empty. + ASSERT_FALSE(callback_a_.is_null()); + ASSERT_FALSE(callback_a_.Equals(null_callback_)); + + callback_a_.Reset(); + + EXPECT_TRUE(callback_a_.is_null()); + EXPECT_TRUE(callback_a_.Equals(null_callback_)); +} + +} // namespace +} // namespace base |