From 7296f2763bd40e560466f09836c7208c42d90f94 Mon Sep 17 00:00:00 2001 From: "ajwong@chromium.org" Date: Mon, 21 Nov 2011 19:23:44 +0000 Subject: Callback API Change: Reimplement Bind(); support IgnoreResult, full currying, and use less types. The main API change IgnoreResult() and fully currying. See unittest for what the new API looks like. The rest of the changes are done to support that. Previously, IgnoreReturn could not be used with WeakPtr<> Bind()s as it was applied after the fact to the Callback object. Now, IgnoreResult() wraps the function like Unretained(). As an incidental benefit, the new implementation gave us fully currying for free. Also, the new implementation scales better when supporting higher arities of functions. The new type growth is: (n^2 + 20n) / 2 as opposed to (3n^2 + 17n) / 2 where n == arity. For n = 6 and n=10, the new implementation has 81 and 155 templates respectively. The old implementation had 105 and 235 templates respectively. BUG=35233,98919,98542 TEST=existing unittests Review URL: http://codereview.chromium.org/8483003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@110975 0039d316-1c4b-4281-b951-d872f2087c98 --- base/bind.h | 442 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 356 insertions(+), 86 deletions(-) (limited to 'base/bind.h') diff --git a/base/bind.h b/base/bind.h index f023308e..1637207 100644 --- a/base/bind.h +++ b/base/bind.h @@ -15,7 +15,10 @@ #include "base/bind_internal.h" #include "base/callback_internal.h" -// See base/callback.h for how to use these functions. +// See base/callback.h for how to use these functions. If reading the +// implementation, before proceeding further, you should read the top +// comment of base/bind_internal.h for a definition of common terms and +// concepts. // // IMPLEMENTATION NOTE // Though Bind()'s result is meant to be stored in a Callback<> type, it @@ -28,116 +31,383 @@ // Each unique combination of (arity, function_type, num_prebound) where // function_type is one of {function, method, const_method} would require // one specialization. We eventually have to do a similar number of -// specializations anyways in the implementation (see the FunctionTraitsN, +// specializations anyways in the implementation (see the Invoker<>, // classes). However, it is avoidable in Bind if we return the result // via an indirection like we do below. +// +// TODO(ajwong): We might be able to avoid this now, but need to test. +// +// It is possible to move most of the COMPILE_ASSERT asserts into BindState<>, +// but it feels a little nicer to have the asserts here so people do not +// need to crack open bind_internal.h. On the other hand, it makes Bind() +// harder to read. namespace base { -template -internal::InvokerStorageHolder > -Bind(Sig f) { - return internal::MakeInvokerStorageHolder( - new internal::InvokerStorage0(f)); -} +template +internal::BindStateHolder< + internal::BindState< + typename internal::FunctorTraits::RunnableType, + typename internal::FunctorTraits::RunType, + void()> > +Bind(Functor functor) { + // Typedefs for how to store and run the functor. + typedef typename internal::FunctorTraits::RunnableType RunnableType; + typedef typename internal::FunctorTraits::RunType RunType; -template -internal::InvokerStorageHolder > -Bind(Sig f, const P1& p1) { - return internal::MakeInvokerStorageHolder( - new internal::InvokerStorage1( - f, p1)); -} + // Use RunnableType::RunType instead of RunType above because our + // checks should below for bound references need to know what the actual + // functor is going to interpret the argument as. + typedef internal::FunctionTraits + BoundFunctorTraits; -template -internal::InvokerStorageHolder > -Bind(Sig f, const P1& p1, const P2& p2) { - return internal::MakeInvokerStorageHolder( - new internal::InvokerStorage2( - f, p1, p2)); -} -template -internal::InvokerStorageHolder > -Bind(Sig f, const P1& p1, const P2& p2, const P3& p3) { - return internal::MakeInvokerStorageHolder( - new internal::InvokerStorage3( - f, p1, p2, p3)); + return internal::MakeBindStateHolder( + new internal::BindState( + internal::MakeRunnable(functor))); } -template -internal::InvokerStorageHolder > -Bind(Sig f, const P1& p1, const P2& p2, const P3& p3, const P4& p4) { - return internal::MakeInvokerStorageHolder( - new internal::InvokerStorage4( - f, p1, p2, p3, p4)); -} +template +internal::BindStateHolder< + internal::BindState< + typename internal::FunctorTraits::RunnableType, + typename internal::FunctorTraits::RunType, + void(typename internal::CallbackParamTraits::StorageType)> > +Bind(Functor functor, const P1& p1) { + // Typedefs for how to store and run the functor. + typedef typename internal::FunctorTraits::RunnableType RunnableType; + typedef typename internal::FunctorTraits::RunType RunType; -template -internal::InvokerStorageHolder > -Bind(Sig f, const P1& p1, const P2& p2, const P3& p3, const P4& p4, - const P5& p5) { - return internal::MakeInvokerStorageHolder( - new internal::InvokerStorage5( - f, p1, p2, p3, p4, p5)); -} + // Use RunnableType::RunType instead of RunType above because our + // checks should below for bound references need to know what the actual + // functor is going to interpret the argument as. + typedef internal::FunctionTraits + BoundFunctorTraits; -template -internal::InvokerStorageHolder > -Bind(Sig f, const P1& p1, const P2& p2, const P3& p3, const P4& p4, - const P5& p5, const P6& p6) { - return internal::MakeInvokerStorageHolder( - new internal::InvokerStorage6( - f, p1, p2, p3, p4, p5, p6)); -} + // Do not allow binding a non-const reference parameter. Non-const reference + // parameters are disallowed by the Google style guide. Also, binding a + // non-const reference parameter can make for subtle bugs because the + // invoked function will receive a reference to the stored copy of the + // argument and not the original. + COMPILE_ASSERT( + !(is_non_const_reference::value ), + do_not_bind_functions_with_nonconst_ref); -// Specializations to allow binding all the free arguments in a -// pre-existing base::Callback<>. This does not give full support for -// currying, but is significantly simpler and addresses the use case -// where a base::Callback<> needs to be invoked on another context/thread. -template -base::Closure Bind(const base::Callback& callback, const P1& p1) { - return base::Bind(&internal::BindMoreFunc1, callback, p1); + // For methods, we need to be careful for parameter 1. We do not require + // a scoped_refptr because BindState<> itself takes care of AddRef() for + // methods. We also disallow binding of an array as the method's target + // object. + COMPILE_ASSERT( + internal::HasIsMethodTag::value || + !internal::NeedsScopedRefptrButGetsRawPtr::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::HasIsMethodTag::value || + !is_array::value, + first_bound_argument_to_method_cannot_be_array); + + return internal::MakeBindStateHolder( + new internal::BindState::StorageType)>( + internal::MakeRunnable(functor), p1)); } -template -base::Closure Bind(const base::Callback& callback, const P1& p1, - const P2& p2) { - return base::Bind(&internal::BindMoreFunc2, callback, p1, p2); +template +internal::BindStateHolder< + internal::BindState< + typename internal::FunctorTraits::RunnableType, + typename internal::FunctorTraits::RunType, + void(typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType)> > +Bind(Functor functor, const P1& p1, const P2& p2) { + // Typedefs for how to store and run the functor. + typedef typename internal::FunctorTraits::RunnableType RunnableType; + typedef typename internal::FunctorTraits::RunType RunType; + + // Use RunnableType::RunType instead of RunType above because our + // checks should below for bound references need to know what the actual + // functor is going to interpret the argument as. + typedef internal::FunctionTraits + BoundFunctorTraits; + + // Do not allow binding a non-const reference parameter. Non-const reference + // parameters are disallowed by the Google style guide. Also, binding a + // non-const reference parameter can make for subtle bugs because the + // invoked function will receive a reference to the stored copy of the + // argument and not the original. + COMPILE_ASSERT( + !(is_non_const_reference::value || + is_non_const_reference::value ), + do_not_bind_functions_with_nonconst_ref); + + // For methods, we need to be careful for parameter 1. We do not require + // a scoped_refptr because BindState<> itself takes care of AddRef() for + // methods. We also disallow binding of an array as the method's target + // object. + COMPILE_ASSERT( + internal::HasIsMethodTag::value || + !internal::NeedsScopedRefptrButGetsRawPtr::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::HasIsMethodTag::value || + !is_array::value, + first_bound_argument_to_method_cannot_be_array); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p2_is_refcounted_type_and_needs_scoped_refptr); + + return internal::MakeBindStateHolder( + new internal::BindState::StorageType, + typename internal::CallbackParamTraits::StorageType)>( + internal::MakeRunnable(functor), p1, p2)); } -template -base::Closure Bind(const base::Callback& callback, const P1& p1, - const P2& p2, const P3& p3) { - return base::Bind(&internal::BindMoreFunc3, callback, p1, - p2, p3); +template +internal::BindStateHolder< + internal::BindState< + typename internal::FunctorTraits::RunnableType, + typename internal::FunctorTraits::RunType, + void(typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType)> > +Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3) { + // Typedefs for how to store and run the functor. + typedef typename internal::FunctorTraits::RunnableType RunnableType; + typedef typename internal::FunctorTraits::RunType RunType; + + // Use RunnableType::RunType instead of RunType above because our + // checks should below for bound references need to know what the actual + // functor is going to interpret the argument as. + typedef internal::FunctionTraits + BoundFunctorTraits; + + // Do not allow binding a non-const reference parameter. Non-const reference + // parameters are disallowed by the Google style guide. Also, binding a + // non-const reference parameter can make for subtle bugs because the + // invoked function will receive a reference to the stored copy of the + // argument and not the original. + COMPILE_ASSERT( + !(is_non_const_reference::value || + is_non_const_reference::value || + is_non_const_reference::value ), + do_not_bind_functions_with_nonconst_ref); + + // For methods, we need to be careful for parameter 1. We do not require + // a scoped_refptr because BindState<> itself takes care of AddRef() for + // methods. We also disallow binding of an array as the method's target + // object. + COMPILE_ASSERT( + internal::HasIsMethodTag::value || + !internal::NeedsScopedRefptrButGetsRawPtr::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::HasIsMethodTag::value || + !is_array::value, + first_bound_argument_to_method_cannot_be_array); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p2_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p3_is_refcounted_type_and_needs_scoped_refptr); + + return internal::MakeBindStateHolder( + new internal::BindState::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType)>( + internal::MakeRunnable(functor), p1, p2, p3)); } -template -base::Closure Bind(const base::Callback& callback, const P1& p1, - const P2& p2, const P3& p3, const P4& p4) { - return base::Bind(&internal::BindMoreFunc4, callback, - p1, p2, p3, p4); +template +internal::BindStateHolder< + internal::BindState< + typename internal::FunctorTraits::RunnableType, + typename internal::FunctorTraits::RunType, + void(typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType)> > +Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + // Typedefs for how to store and run the functor. + typedef typename internal::FunctorTraits::RunnableType RunnableType; + typedef typename internal::FunctorTraits::RunType RunType; + + // Use RunnableType::RunType instead of RunType above because our + // checks should below for bound references need to know what the actual + // functor is going to interpret the argument as. + typedef internal::FunctionTraits + BoundFunctorTraits; + + // Do not allow binding a non-const reference parameter. Non-const reference + // parameters are disallowed by the Google style guide. Also, binding a + // non-const reference parameter can make for subtle bugs because the + // invoked function will receive a reference to the stored copy of the + // argument and not the original. + COMPILE_ASSERT( + !(is_non_const_reference::value || + is_non_const_reference::value || + is_non_const_reference::value || + is_non_const_reference::value ), + do_not_bind_functions_with_nonconst_ref); + + // For methods, we need to be careful for parameter 1. We do not require + // a scoped_refptr because BindState<> itself takes care of AddRef() for + // methods. We also disallow binding of an array as the method's target + // object. + COMPILE_ASSERT( + internal::HasIsMethodTag::value || + !internal::NeedsScopedRefptrButGetsRawPtr::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::HasIsMethodTag::value || + !is_array::value, + first_bound_argument_to_method_cannot_be_array); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p2_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p3_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p4_is_refcounted_type_and_needs_scoped_refptr); + + return internal::MakeBindStateHolder( + new internal::BindState::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType)>( + internal::MakeRunnable(functor), p1, p2, p3, p4)); } -template -base::Closure Bind(const base::Callback& callback, const P1& p1, - const P2& p2, const P3& p3, const P4& p4, const P5& p5) { - return base::Bind(&internal::BindMoreFunc5, - callback, p1, p2, p3, p4, p5); +internal::BindStateHolder< + internal::BindState< + typename internal::FunctorTraits::RunnableType, + typename internal::FunctorTraits::RunType, + void(typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType)> > +Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5) { + // Typedefs for how to store and run the functor. + typedef typename internal::FunctorTraits::RunnableType RunnableType; + typedef typename internal::FunctorTraits::RunType RunType; + + // Use RunnableType::RunType instead of RunType above because our + // checks should below for bound references need to know what the actual + // functor is going to interpret the argument as. + typedef internal::FunctionTraits + BoundFunctorTraits; + + // Do not allow binding a non-const reference parameter. Non-const reference + // parameters are disallowed by the Google style guide. Also, binding a + // non-const reference parameter can make for subtle bugs because the + // invoked function will receive a reference to the stored copy of the + // argument and not the original. + COMPILE_ASSERT( + !(is_non_const_reference::value || + is_non_const_reference::value || + is_non_const_reference::value || + is_non_const_reference::value || + is_non_const_reference::value ), + do_not_bind_functions_with_nonconst_ref); + + // For methods, we need to be careful for parameter 1. We do not require + // a scoped_refptr because BindState<> itself takes care of AddRef() for + // methods. We also disallow binding of an array as the method's target + // object. + COMPILE_ASSERT( + internal::HasIsMethodTag::value || + !internal::NeedsScopedRefptrButGetsRawPtr::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::HasIsMethodTag::value || + !is_array::value, + first_bound_argument_to_method_cannot_be_array); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p2_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p3_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p4_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p5_is_refcounted_type_and_needs_scoped_refptr); + + return internal::MakeBindStateHolder( + new internal::BindState::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType)>( + internal::MakeRunnable(functor), p1, p2, p3, p4, p5)); } -template -base::Closure Bind(const base::Callback& callback, const P1& p1, - const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) { - return base::Bind(&internal::BindMoreFunc6, - callback, p1, p2, p3, p4, p5, p6); +internal::BindStateHolder< + internal::BindState< + typename internal::FunctorTraits::RunnableType, + typename internal::FunctorTraits::RunType, + void(typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType)> > +Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5, const P6& p6) { + // Typedefs for how to store and run the functor. + typedef typename internal::FunctorTraits::RunnableType RunnableType; + typedef typename internal::FunctorTraits::RunType RunType; + + // Use RunnableType::RunType instead of RunType above because our + // checks should below for bound references need to know what the actual + // functor is going to interpret the argument as. + typedef internal::FunctionTraits + BoundFunctorTraits; + + // Do not allow binding a non-const reference parameter. Non-const reference + // parameters are disallowed by the Google style guide. Also, binding a + // non-const reference parameter can make for subtle bugs because the + // invoked function will receive a reference to the stored copy of the + // argument and not the original. + COMPILE_ASSERT( + !(is_non_const_reference::value || + is_non_const_reference::value || + is_non_const_reference::value || + is_non_const_reference::value || + is_non_const_reference::value || + is_non_const_reference::value ), + do_not_bind_functions_with_nonconst_ref); + + // For methods, we need to be careful for parameter 1. We do not require + // a scoped_refptr because BindState<> itself takes care of AddRef() for + // methods. We also disallow binding of an array as the method's target + // object. + COMPILE_ASSERT( + internal::HasIsMethodTag::value || + !internal::NeedsScopedRefptrButGetsRawPtr::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::HasIsMethodTag::value || + !is_array::value, + first_bound_argument_to_method_cannot_be_array); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p2_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p3_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p4_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p5_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, + p6_is_refcounted_type_and_needs_scoped_refptr); + + return internal::MakeBindStateHolder( + new internal::BindState::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType, + typename internal::CallbackParamTraits::StorageType)>( + internal::MakeRunnable(functor), p1, p2, p3, p4, p5, p6)); } } // namespace base -- cgit v1.1