Chromium Code Reviews| Index: base/callback_list.h |
| diff --git a/base/callback_list.h b/base/callback_list.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d6c2edcac8bac1be2185051f3907f017dbf741f9 |
| --- /dev/null |
| +++ b/base/callback_list.h |
| @@ -0,0 +1,208 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
|
Avi (use Gerrit)
2013/08/21 19:56:18
No (c), 2013.
For all new files.
Cait (Slow)
2013/08/22 15:30:25
Done.
|
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#ifndef BASE_CALLBACK_LIST_H__ |
| +#define BASE_CALLBACK_LIST_H__ |
| + |
| +#include <algorithm> |
| +#include <limits> |
| +#include <vector> |
| + |
| +#include "base/basictypes.h" |
| +#include "base/bind.h" |
| +#include "base/callback.h" |
| +#include "base/memory/weak_ptr.h" |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| +// |
| +// OVERVIEW: |
| +// |
| +// A container for a list of callbacks. Unlike a normal STL vector or list, |
| +// this container can be modified during iteration without invalidating the |
| +// iterator. So, it safely handles the case of an callback removing itself |
| +// or other callback from the list while callbacks are being run. |
| +// |
| +// TYPICAL USAGE: |
| +// |
| +// class MyWidget { |
| +// public: |
| +// ... |
| +// |
| +// typedef base::Callback<void(Foo*)> OnFooCallback; |
| +// |
| +// base::Closure RegisterCallback(const OnFooCallback& cb) { |
| +// return callback_list_.RegisterCallback(cb); |
| +// } |
| +// |
| +// void NotifyFoo(Foo* foo) { |
| +// FOR_EACH_CALLBACK(OnFooCallback, callback_list_, foo); |
| +// } |
| +// |
| +// private: |
| +// CallbackList<OnFooCallback> callback_list_; |
| +// }; |
| +// |
| +// |
| +// class MyWidgetListener { |
| +// public: |
| +// |
| +// MyWidgetListener::MyWidgetListener(){ |
| +// remove_foo_callback_ = MyWidget::GetCurrent()->RegisterCallback( |
| +// base::Bind(&MyWidgetListener::OnFoo, this)); |
| +// } |
| +// |
| +// MyWidgetListener::~MyWidgetListener() { |
| +// if (!remove_foo_callback_.is_null()) |
| +// remove_foo_callback_.Run(); |
|
Avi (use Gerrit)
2013/08/21 19:56:18
Grr. Not a fan of is_null. Drop it; I have a sugge
Cait (Slow)
2013/08/22 15:30:25
Done -- there are a couple more uses of is_null(),
Avi (use Gerrit)
2013/08/22 16:25:42
I don't mind internal use of is_null. But when you
|
| +// } |
| +// |
| +// void OnFoo(Foo* foo) { |
| +// // do something. |
| +// } |
| +// |
| +// private: |
| +// base::Closure remove_foo_callback_; |
| +// }; |
| +// |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| + template <class SIG> |
|
Avi (use Gerrit)
2013/08/21 19:56:18
no indent
Cait (Slow)
2013/08/22 15:30:25
Done.
|
| +class CallbackListBase { |
| + public: |
| + // An iterator class that can be used to access the list of callbacks. See |
| + // also the FOR_EACH_CALLBACK macro defined below. |
| + class Iterator { |
| + public: |
| + Iterator(CallbackListBase<SIG>& list) |
| + : list_(list.GetWeakPtr()), |
| + index_(0) { |
| + ++list_->notify_depth_; |
| + } |
| + |
| + ~Iterator() { |
| + if (list_.get() && --list_->notify_depth_ == 0) |
| + list_->Compact(); |
| + } |
| + |
| + SIG GetNext() { |
| + if (!list_.get()) |
| + return SIG(); |
| + ListType& callbacks = list_->callbacks_; |
| + return index_ < callbacks.size() ? callbacks[index_++] : SIG(); |
| + } |
| + |
| + bool HasNext() { |
| + if (!list_.get()) |
| + return false; |
| + return index_ < list_->callbacks_.size(); |
| + } |
| + |
| + private: |
| + base::WeakPtr<CallbackListBase<SIG> > list_; |
| + size_t index_; |
| + }; |
| + |
| + CallbackListBase() : notify_depth_(0), |
| + weak_factory_(this) {} |
| + |
| + // Add an callback to the list. An callback should not be added to |
| + // the same list more than once. |
|
Avi (use Gerrit)
2013/08/21 19:56:18
Guarantee in the comment that you can always safel
Cait (Slow)
2013/08/22 15:30:25
Done.
|
| + base::Closure RegisterCallback(const SIG& cb) { |
| + for (size_t i = 0; i < callbacks_.size(); i++) { |
| + if (callbacks_[i].Equals(cb)) { |
| + NOTREACHED() << "Callbacks can only be added once!"; |
| + return base::Closure(); |
|
Avi (use Gerrit)
2013/08/21 19:56:18
... and rather than returning base::Closure(), ret
Cait (Slow)
2013/08/22 15:30:25
Done.
|
| + } |
| + } |
| + callbacks_.push_back(cb); |
| + base::Closure dereg = base::Bind( |
| + &CallbackListBase::RemoveCallback, GetWeakPtr(), cb); |
| + return dereg; |
| + } |
| + |
| + // Remove an callback from the list if it is in the list. |
| + void RemoveCallback(const SIG& cb) { |
| + for (size_t i = 0; i < callbacks_.size(); i++) { |
| + if (callbacks_[i].Equals(cb)) { |
| + if (notify_depth_) |
| + callbacks_[i].Reset(); |
| + else |
| + callbacks_.erase(callbacks_.begin() + i); |
| + return; |
| + } |
| + } |
| + } |
| + |
| + bool HasCallback(const SIG& cb) const { |
| + for (size_t i = 0; i < callbacks_.size(); ++i) { |
| + if (callbacks_[i].Equlas(cb)) |
| + return true; |
| + } |
| + return false; |
| + } |
| + |
| + void Clear() { |
| + if (notify_depth_) { |
| + for (size_t i = 0; i < callbacks_.size(); ++i) |
| + callbacks_[i].Reset(); |
| + } else { |
| + callbacks_.clear(); |
| + } |
| + } |
| + |
| + size_t size() const { return callbacks_.size(); } |
| + |
| + protected: |
| + base::WeakPtr<CallbackListBase<SIG>> GetWeakPtr() { |
| + return weak_factory_.GetWeakPtr(); |
| + } |
| + |
| + void Compact() { |
| + for (size_t i = 0; i < callbacks_.size(); i++) { |
| + if (callbacks_[i].is_null()) { |
| + callbacks_.erase(callbacks_.begin() + i); |
| + } |
| + } |
| + } |
| + |
| + private: |
| + friend class CallbackListBase::Iterator; |
| + typedef std::vector<SIG> ListType; |
| + ListType callbacks_; |
| + int notify_depth_; |
| + base::WeakPtrFactory<CallbackListBase<SIG>> weak_factory_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(CallbackListBase); |
| +}; |
| + |
| +template <class SIG, bool check_empty = false> |
| +class CallbackList : public CallbackListBase<SIG> { |
| + public: |
| + CallbackList() {} |
| + |
| + ~CallbackList() { |
| + // When check_empty is true, assert that the list is empty on destruction. |
| + if (check_empty) { |
| + DCHECK_EQ(CallbackListBase<SIG>::size(), 0U); |
| + } |
| + } |
| + |
| + bool might_have_callbacks() const { |
| + return CallbackListBase<SIG>::size() != 0; |
| + } |
| +}; |
| + |
| +#define FOR_EACH_CALLBACK(CallbackType, callback_list, ...) \ |
| + do { \ |
| + if ((callback_list).might_have_callbacks()) { \ |
| + CallbackListBase<CallbackType>::Iterator it(callback_list); \ |
| + CallbackType cb; \ |
| + while(it.HasNext()) { \ |
| + if(!(cb = it.GetNext()).is_null()) \ |
| + cb.Run(__VA_ARGS__ ) ; \ |
| + } \ |
| + } \ |
| + } while (0) |
| + |
| +#endif // BASE_CALLBACK_LIST_H__ |