Chromium Code Reviews| Index: base/prebind.h |
| diff --git a/base/prebind.h b/base/prebind.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..489921683cdf497b3859e46342ea311973b2642d |
| --- /dev/null |
| +++ b/base/prebind.h |
| @@ -0,0 +1,402 @@ |
| +// Copyright (c) 2011 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_PREBIND_H |
| +#define BASE_PREBIND_H |
| + |
| +#include <tr1/functional> |
| + |
| +#include <base/ref_counted.h> |
| + |
| +namespace base { |
| + |
| +// These are needed to implement a wrapper that disables refcounting on |
| +// objects used with a Thunk. |
| +template <typename O> |
| +class UnretainedWrapper { |
| + public: |
| + explicit UnretainedWrapper(O* o) : obj_(o) {} |
| + O* get() const { return obj_; } |
| + |
| + private: |
| + O* obj_; |
| +}; |
| + |
| +template <typename O> |
| +UnretainedWrapper<O> Unretained(O* o) { |
| + return UnretainedWrapper<O>(o); |
| +} |
| + |
| +// Invoker is adds a level of indirection into the function call using syntax |
| +// that will work with either a raw pointer to an object, or a scoped_refptr. |
| +// |
| +// This allows us to pass a scoped_refptr into bind for an object we wish to |
| +// retrain and maintain the same syntax for invoking its methods. |
| +// |
| +// O is either a scoped_refptr<T> or T* where T is the target object. |
| +template <typename O, typename Sig> |
| +struct Invoker; |
| + |
| +template <typename O, typename R, typename T> |
| +struct Invoker<O, R(T::*)(void)> { |
| + static R invoke(R(T::*f)(void), const O& o) { |
| + return (o->*f)(); |
| + } |
| +}; |
| + |
| +template <typename O, typename R, typename T, typename A0> |
| +struct Invoker<O, R(T::*)(A0)> { |
| + static R invoke(R(T::*f)(A0), const O& o, const A0& a0) { |
| + return (o->*f)(a0); |
| + } |
| +}; |
| + |
| +template <typename O, typename R, typename T, typename A0, typename A1> |
| +struct Invoker<O, R(T::*)(A0, A1)> { |
| + static R invoke(R(T::*f)(A0, A1), const O& o, const A0& a0, const A1& a1) { |
| + return (o->*f)(a0, a1); |
| + } |
| +}; |
| + |
| +template <typename O, typename R, typename T, typename A0, typename A1, |
| + typename A2> |
| +struct Invoker<O, R(T::*)(A0, A1, A2)> { |
| + static R invoke(R(T::*f)(A0, A1, A2), const O& o, const A0& a0, const A1& a1, |
| + const A2& a2) { |
| + return (o->*f)(a0, a1, a2); |
| + } |
| +}; |
| + |
| +template <typename O, typename R, typename T, typename A0, typename A1, |
| + typename A2, typename A3> |
| +struct Invoker<O, R(T::*)(A0, A1, A2, A3)> { |
| + static R invoke(R(T::*f)(A0, A1, A2, A3), const O& o, const A0& a0, |
| + const A1& a1, const A2& a2, const A3& a3) { |
| + return (o->*f)(a0, a1, a2, a3); |
| + } |
| +}; |
| + |
| +template <typename O, typename R, typename T, typename A0, typename A1, |
| + typename A2, typename A3, typename A4> |
| +struct Invoker<O, R(T::*)(A0, A1, A2, A3, A4)> { |
| + static R invoke(R(T::*f)(A0, A1, A2, A3, A4), const O& o, const A0& a0, |
| + const A1& a1, const A2& a2, const A3& a3, const A4& a4) { |
| + return (o->*f)(a0, a1, a2, a3, a4); |
| + } |
| +}; |
| + |
| +template <typename O> |
| +struct RetainTraits; |
| + |
| +template <typename O> |
| +struct RetainTraits<O*> { |
| + // TODO(ajwong): Do we need the "type" typedef? Can't we just use O* in |
| + // prebind since O != T? |
| + typedef O type; |
| + typedef scoped_refptr<O> prebind_type; |
| + |
| + static O* unwrap(O* o) { return o; } |
| + static prebind_type MaybeScopedRefptr(O* o) { return o; } |
| +}; |
| + |
| +template <typename O> |
| +struct RetainTraits<UnretainedWrapper<O> > { |
| + typedef O type; |
| + typedef O* prebind_type; |
| + |
| + static O* unwrap(const UnretainedWrapper<O>& o) { return o.get(); } |
| + static prebind_type MaybeScopedRefptr(O* o) { return o; } |
| +}; |
| + |
| +template <typename Sig> |
| +class Thunk; |
| + |
| +// TODO(ajwong): Thunk should have a pointer that can be set to refer to a |
| +// "tracked" object. This should be set/unset by messageloop when it creates |
| +// the pending task. That should give nearly equivalent functionality to |
| +// Task inheriting from Tracked. |
| +template <typename R> |
| +class Thunk<R(void)> { |
| + public: |
| + typedef std::tr1::function<R(void)> ThunkType; |
| + ThunkType f_; |
| + |
| + explicit Thunk(ThunkType f) : f_(f) {} |
| + |
| + R operator()(void) { |
| + return f_(); |
| + } |
| +}; |
| + |
| +template <typename R, typename A0> |
| +class Thunk<R(A0)> { |
| + public: |
| + typedef std::tr1::function<R(A0)> ThunkType; |
| + ThunkType f_; |
| + |
| + explicit Thunk(ThunkType f) : f_(f) {} |
| + |
| + R operator()(A0& a0) { |
| + return f_(a0); |
| + } |
| +}; |
| + |
| +template <typename R, typename A0, typename A1> |
| +class Thunk<R(A0, A1)> { |
| + public: |
| + typedef std::tr1::function<R(A0,A1)> ThunkType; |
| + ThunkType f_; |
| + std::tr1::function<void(void)> cleanup_; |
| + |
| + explicit Thunk(ThunkType f) : f_(f) {} |
| + |
| + R operator()(const A0& a0, const A1& a1) { |
| + return f_(a0, a1); |
| + } |
| +}; |
| + |
| +// Note that when declaring these template parameters, the types used in the |
| +// function signature MUST not be shared with the types used in the arguments. |
| +// If they are shared, then automatic conversions break. For example, this |
| +// should work: |
| +// |
| +// void foo(double d); |
| +// function<void(void)> f = Prebind(&foo, 2); |
| +// |
| +// However, if you declare the template for prebind as follows: |
| +// |
| +// template <typename R, typename P0> |
| +// function<R(void)> Prebind(R(*)(P0), P0 p0); |
| +// |
| +// Then the line invoking Prebind will fail because 2 is an integer, and P0 is |
| +// locked to be a double. If instead, you declare the template to not tie the |
| +// function signature directly to the parameters, the compiler will have the |
| +// flexibility to do the right conversion. Thus, the correct declaration will |
| +// look like this: |
| +// |
| +// template <typename R, typename X0, typename P0> |
| +// function<R(void)> Prebind(R(*)(X0), P0 p0); |
| +// |
| +// The signature uses the type X0, and the argument uses the type P0. There is |
| +// no directly relationship enforced by the template declaration. Instead, we |
| +// rely on the compiler to output in an error of P0 is not converatble to X0. |
| +// |
| + |
| + |
| +// 1 -> 0 |
| +template <typename R, typename X0, typename P0> |
| +Thunk<R(void)> |
| +Prebind(R(*f)(X0), P0 p0) { |
| + return Thunk<R(void)>(std::tr1::bind(f, p0)); |
| +} |
| + |
| +// 2 -> 0 |
| +template <typename R, typename X0, typename X1, typename P0, typename P1> |
| +Thunk<R(void)> |
| +Prebind(R(*f)(X0, X1), P0 p0, P1 p1) { |
| + return Thunk<R(void)>(std::tr1::bind(f, p0, p1)); |
| +} |
| + |
| +// 2 -> 1 |
| +template <typename R, typename X0, typename P0, typename A0> |
| +Thunk<R(A0)> |
| +Prebind(R(*f)(X0, A0), P0 p0) { |
| + return Thunk<R(A0)>(std::tr1::bind(f, p0, std::tr1::placeholders::_1)); |
| +} |
| + |
| +// (curry) 1 -> 0 |
| +template <typename R, typename X0, typename P0> |
| +Thunk<R(void)> |
| +Prebind(Thunk<R(X0)> f, P0 p0) { |
| + return Thunk<R(void)>(std::tr1::bind(f, p0)); |
| +} |
| + |
| +// (curry) 2 -> 0 |
| +template <typename R, typename X0, typename X1, typename P0, typename P1> |
| +Thunk<R(void)> |
| +Prebind(Thunk<R(X0, X1)> f, P0 p0, P1 p1) { |
| + return Thunk<R(void)>(std::tr1::bind(f, p0, p1)); |
| +} |
| + |
| +// (curry) 2 -> 1 |
| +template <typename R, typename X0, typename P0, typename A0> |
| +Thunk<R(A0)> |
| +Prebind(Thunk<R(X0, A0)> f, P0 p0) { |
| + return Thunk<R(A0)>(std::tr1::bind(f, p0, std::tr1::placeholders::_1)); |
| +} |
| + |
| +// Method 0 -> 0 |
| +template <typename R, typename O, typename T> |
| +Thunk<R(void)> |
| +Prebind(R(O::*f)(), T t) { |
| + typename RetainTraits<T>::type* object_(RetainTraits<T>::unwrap(t)); |
|
willchan no longer on Chromium
2011/01/05 19:44:09
Here you take T, which may be scoped_refptr<O>, an
|
| + return Thunk<R(void)>( |
| + std::tr1::bind( |
| + &Invoker<typename RetainTraits<T>::prebind_type, |
| + R(O::*)(void)>::invoke, |
| + f, |
| + RetainTraits<T>::MaybeScopedRefptr(object_))); |
|
willchan no longer on Chromium
2011/01/05 19:44:09
MaybeScopedRefptr() basically always forces using
|
| +} |
| + |
| +// Method 1 -> 0 |
| +template <typename R, typename O, typename T, typename X0, typename P0> |
| +Thunk<R(void)> |
| +Prebind(R(O::*f)(X0), T t, P0 p0) { |
| + typename RetainTraits<T>::type* object_(RetainTraits<T>::unwrap(t)); |
| + |
| + return Thunk<R(void)>( |
| + std::tr1::bind( |
| + &Invoker<typename RetainTraits<T>::prebind_type, |
| + R(O::*)(X0)>::invoke, |
| + f, |
| + RetainTraits<T>::MaybeScopedRefptr(object_), |
| + p0)); |
| +} |
| + |
| +// Method 1 -> 1 |
| +template <typename R, typename O, typename T, typename A0> |
| +Thunk<R(A0)> |
| +Prebind(R(O::*f)(A0), T t) { |
| + typename RetainTraits<T>::type* object_(RetainTraits<T>::unwrap(t)); |
| + |
| + return Thunk<R(void)>( |
| + std::tr1::bind( |
| + &Invoker<typename RetainTraits<T>::prebind_type, |
| + R(O::*)(A0)>::invoke, |
| + f, |
| + RetainTraits<T>::MaybeScopedRefptr(object_), |
| + std::tr1::placeholders::_1)); |
| +} |
| + |
| +// Method 2 -> 0 |
| +template <typename R, typename O, typename T, typename X0, typename X1, |
| + typename P0, typename P1> |
| +Thunk<R(void)> |
| +Prebind(R(O::*f)(X0, X1), T t, P0 p0, P1 p1) { |
| + typename RetainTraits<T>::type* object_(RetainTraits<T>::unwrap(t)); |
| + return Thunk<R(void)>( |
| + std::tr1::bind( |
| + &Invoker<typename RetainTraits<T>::prebind_type, |
| + R(O::*)(X0,X1)>::invoke, |
| + f, |
| + RetainTraits<T>::MaybeScopedRefptr(object_), |
| + p0, |
| + p1)); |
| +} |
| + |
| +// Method 2 -> 1 |
| +template <typename R, typename O, typename T, typename X0, typename P0, |
| + typename A0> |
| +Thunk<R(A0)> |
| +Prebind(R(O::*f)(X0, A0), T t, P0 p0) { |
| + typename RetainTraits<T>::type* object_(RetainTraits<T>::unwrap(t)); |
| + return Thunk<R(A0)>( |
| + std::tr1::bind( |
| + &Invoker<typename RetainTraits<T>::prebind_type, |
| + R(O::*)(X0, A0)>::invoke, |
| + f, |
| + RetainTraits<T>::MaybeScopedRefptr(object_), |
| + p0, |
| + std::tr1::placeholders::_1)); |
| +} |
| + |
| +// Method 2 -> 2 |
| +template <typename R, typename O, typename T, typename A0, typename A1> |
| +Thunk<R(A0, A1)> |
| +Prebind(R(O::*f)(A0, A1), T t) { |
| + typename RetainTraits<T>::type* object_(RetainTraits<T>::unwrap(t)); |
| + |
| + return Thunk<R(A0, A1)>( |
| + std::tr1::bind( |
| + &Invoker<typename RetainTraits<T>::prebind_type, |
| + R(O::*)(A0, A1)>::invoke, |
| + f, |
| + RetainTraits<T>::MaybeScopedRefptr(object_), |
| + std::tr1::placeholders::_1, |
| + std::tr1::placeholders::_2)); |
| +} |
| + |
| +// Method 5 -> 2 ...yeah, I skipped a few...got bored of typing it. |
| +template <typename R, typename O, typename T, |
| + typename X0, typename X1, typename X2, |
| + typename P0, typename P1, typename P2, |
| + typename A0, typename A1> |
| +Thunk<R(A0, A1)> |
| +Prebind(R(O::*f)(X0, X1, X2, A0, A1), T t, P0 p0, P1 p1, P2 p2) { |
| + typename RetainTraits<T>::type* object_(RetainTraits<T>::unwrap(t)); |
| + |
| + return Thunk<R(A0, A1)>( |
| + std::tr1::bind( |
| + &Invoker<typename RetainTraits<T>::prebind_type, |
| + R(O::*)(X0, X1, X2, A0, A1)>::invoke, |
| + f, |
| + RetainTraits<T>::MaybeScopedRefptr(object_), |
| + p0, p1, p2, |
| + std::tr1::placeholders::_1, |
| + std::tr1::placeholders::_2)); |
| +} |
| + |
| +// Wraps a Thunk to automatically cancel a task when the ThunkCanceller is |
| +// deleted. This allows a caller to "nop" all outstanding callbacks registered |
| +// with the ThunkCanceller. |
| +// |
| +// Note that if you're also looking at the Closure code, this is nearly |
| +// identical to ClosureCanceller. In fact, it can be used interchangeably, but |
| +// I put a forked version here just for completeness. |
| +class ThunkCanceller { |
| + public: |
| + ThunkCanceller() : cancel_state_(new CancelState()) {} |
| + |
| + ~ThunkCanceller() { |
| + cancel_state_->is_canceled = true; |
| + } |
| + |
| + template <typename T> |
| + Thunk<void(void)> Wrap(T c) { |
| + using std::tr1::bind; |
| + return base::Thunk<void(void)>( |
| + Prebind(&ThunkCanceller::Run<T>, cancel_state_, c)); |
| + } |
| + |
| + bool empty() const { |
| + // The ThunkCanceller has the only reference, no tasks are outstanding. |
| + return cancel_state_->HasOneRef(); |
| + } |
| + |
| + void RevokeAll() { |
| + // Cancel all outstanding, then create a new cancel state so this object may |
| + // be reused. |
| + cancel_state_->is_canceled = true; |
| + cancel_state_ = new CancelState(); |
| + } |
| + |
| + private: |
| + // The ecopedRunnableMethodFactory uses a WeakPtr. This is because it is |
| + // actually reimplementing the storage for the task object, so a pointer is |
| + // necessary and thus WeakPtr can be overloaded to serve as a flag. |
| + // |
| + // In this design, it seems overkill to use WeakPtr instead of a simple flag |
| + // class (which WeakPtr eventually devolves into anyways). |
| + class CancelState : public RefCounted<CancelState> { |
| + public: |
| + CancelState() : is_canceled(false) {} |
| + |
| + bool is_canceled; |
| + }; |
| + |
| + template <typename T> |
| + static void Run(scoped_refptr<CancelState> cancel_state, T c) { |
| + if (!cancel_state->is_canceled) { |
| + c(); |
| + } |
| + } |
| + |
| + scoped_refptr<CancelState> cancel_state_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ThunkCanceller); |
| +}; |
| + |
| +} // namespace base |
| + |
| +#endif // BASE_PREBIND_H |