Index: base/prebind.h |
diff --git a/base/prebind.h b/base/prebind.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0b987d871595b42e54eac95121318bffc9009554 |
--- /dev/null |
+++ b/base/prebind.h |
@@ -0,0 +1,301 @@ |
+// Copyright (c) 2009 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> |
+#include <base/closure.h> // For UnretainedWrapper |
+#include <base/tracked.h> |
+ |
+namespace base { |
+ |
+class ThunkState : public RefCountedThreadSafe<ThunkState> { |
+ public: |
+ tracked_objects::Tracked tracked_; |
willchan no longer on Chromium
2011/01/05 00:59:01
Similar comment as I made on the ClosureState thin
awong
2011/01/05 03:17:42
Okay, I whacked it for now.
|
+ |
+ // If we used a list here, we'd have really nice resource management. |
+ ::std::tr1::function<void(void)> cleanup_; |
+ |
+ template <typename T> |
+ void RetainObject(T* obj) { |
+ obj->AddRef(); |
+ cleanup_ = ::std::tr1::bind(&T::Release, obj); |
+ } |
+}; |
+ |
+template <typename O> |
+struct RetainTraits; |
+ |
+template <typename O> |
+struct RetainTraits<O*> { |
+ typedef O type; |
+ static O* unwrap(O* o) { return o; } |
+ static void RetainObject(ThunkState* state, O* o) { |
+ state->RetainObject(o); |
+ } |
+}; |
+ |
+template <typename O> |
+struct RetainTraits<UnretainedWrapper<O> > { |
+ typedef O type; |
+ static O* unwrap(const UnretainedWrapper<O>& o) { return o.get(); } |
+ static void RetainObject(ThunkState* state, O* o) {} |
+}; |
+ |
+template <typename Sig> |
+class Thunk; |
willchan no longer on Chromium
2011/01/05 00:59:01
I'm not sure if thunk is as commonly used a term a
awong
2011/01/05 03:17:42
I don't like Thunk either. After we settle on a p
|
+ |
+template <typename R> |
+class Thunk<R(void)> { |
+ public: |
+ typedef ::std::tr1::function<R(void)> ThunkType; |
+ ThunkType f_; |
+ scoped_refptr<ThunkState> state_; |
+ |
+ explicit Thunk(ThunkType f) : f_(f), state_(new ThunkState()) {} |
+ |
+ R operator()(void) { |
+ return f_(); |
+ } |
+}; |
+ |
+template <typename R, typename A0> |
+class Thunk<R(A0)> { |
+ public: |
+ typedef ::std::tr1::function<R(A0)> ThunkType; |
+ ThunkType f_; |
+ scoped_refptr<ThunkState> state_; |
+ |
+ explicit Thunk(ThunkType f) : f_(f), state_(new ThunkState()) {} |
+ |
+ 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_; |
+ scoped_refptr<ThunkState> state_; |
+ |
+ explicit Thunk(ThunkType f) : f_(f), state_(new ThunkState()) {} |
+ |
+ 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)(P0), 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)); |
+ Thunk<R(void)> thunk(::std::tr1::bind(f, object_)); |
+ RetainTraits<T>::RetainObject(thunk.state_.get(), object_); |
+ return thunk; |
+} |
+ |
+// 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)); |
+ Thunk<R(void)> thunk(::std::tr1::bind(f, object_, p0)); |
+ RetainTraits<T>::RetainObject(thunk.state_.get(), object_); |
+ return thunk; |
+} |
+ |
+// 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)); |
+ Thunk<R(A0)> thunk(::std::tr1::bind(f, object_)); |
+ RetainTraits<T>::RetainObject(thunk.state_.get(), object_); |
+ return thunk; |
+} |
+ |
+// 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)); |
+ Thunk<R(void)> thunk(::std::tr1::bind(f, object_, p0, p1)); |
+ RetainTraits<T>::RetainObject(thunk.state_.get(), object_); |
+ return thunk; |
+} |
+ |
+// 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)); |
+ Thunk<R(A0)> thunk(::std::tr1::bind(f, object_, p0, ::std::tr1::placeholders::_1)); |
+ RetainTraits<T>::RetainObject(thunk.state_.get(), object_); |
+ return thunk; |
+} |
+ |
+// 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)); |
+ Thunk<R(A0,A1)> thunk(::std::tr1::bind(f, object_, |
+ ::std::tr1::placeholders::_1, |
+ ::std::tr1::placeholders::_2)); |
+ RetainTraits<T>::RetainObject(thunk.state_.get(), object_); |
+ return thunk; |
+} |
+ |
+// 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) { |
+ O* object_(RetainTraits<T>::unwrap(t)); |
+ Thunk<R(A0,A1)> thunk(::std::tr1::bind(f, object_, p0, p1, p2, |
+ ::std::tr1::placeholders::_1, |
+ ::std::tr1::placeholders::_2)); |
+ RetainTraits<T>::RetainObject(thunk.state_.get(), object_); |
+ return thunk; |
+} |
+ |
+// 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 ScopedRunnableMethodFactory 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_; |
+}; |
+ |
+} // namespace base |
+ |
+#endif // BASE_PREBIND_H |