Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1695)

Unified Diff: base/callback_list.h

Issue 22877038: Add a CallbackRegistry class to base/ to manage callbacks (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: base/callback_list.h
diff --git a/base/callback_list.h b/base/callback_list.h
new file mode 100644
index 0000000000000000000000000000000000000000..94de8f58c78e4d644265795b2de2bc98f99e71c5
--- /dev/null
+++ b/base/callback_list.h
@@ -0,0 +1,206 @@
+// Copyright 2013 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_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);
+// }
+//
+// private:
+// void NotifyFoo(Foo* foo) {
+// FOR_EACH_CALLBACK(OnFooCallback, callback_list_, foo);
+// }
+//
+// CallbackList<OnFooCallback> callback_list_;
+// };
+//
+//
+// class MyWidgetListener {
+// public:
+//
+// MyWidgetListener::MyWidgetListener() {
+// remove_foo_callback_ = MyWidget::GetCurrent()->RegisterCallback(
+// base::Bind(&MyWidgetListener::OnFoo, this));
+// }
+//
+// MyWidgetListener::~MyWidgetListener() {
+// remove_foo_callback_.Run();
+// }
+//
+// void OnFoo(Foo* foo) {
+// // Do something.
+// }
+//
+// private:
+// base::Closure remove_foo_callback_;
+// };
+//
+///////////////////////////////////////////////////////////////////////////////
+
+template <class SIG>
+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_->active_iterator_count_;
+ }
+
+ ~Iterator() {
+ if (list_.get() && --list_->active_iterator_count_ == 0)
+ list_->Compact();
+ }
+
+ SIG GetNext() {
+ if (!list_.get())
+ return SIG();
+ ListType& callbacks = list_->callbacks_;
+ while (index_ < callbacks.size() && callbacks[index_].is_null())
+ ++index_;
+ return index_ < callbacks.size() ? callbacks[index_++] : SIG();
+ }
+
+ private:
+ base::WeakPtr<CallbackListBase<SIG> > list_;
+ size_t index_;
+ };
+
+ CallbackListBase() : active_iterator_count_(0),
+ weak_factory_(this) {}
+
+ // Add a callback to the list. A callback should not be added to
+ // the same list more than once. The returned closure (used to remove the
+ // callback from the list) is guaranteed to be safe to run.
+ base::Closure RegisterCallback(const SIG& cb) {
+ if (cb.is_null())
+ return base::Bind(&base::DoNothing);
Avi (use Gerrit) 2013/08/23 16:51:12 Now that is defensive programming. I'm actually la
Cait (Slow) 2013/08/23 18:21:10 Done.
+
+ for (size_t i = 0; i < callbacks_.size(); i++) {
+ if (callbacks_[i].Equals(cb)) {
+ NOTREACHED() << "Callbacks can only be added once!";
+ return base::Bind(&base::DoNothing);
+ }
+ }
+ callbacks_.push_back(cb);
+ base::Closure removal_cb = base::Bind(
+ &CallbackListBase::RemoveCallback, GetWeakPtr(), cb);
+ return removal_cb;
+ }
+
+ // 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 (active_iterator_count_)
+ 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].Equals(cb))
+ return true;
+ }
+ return false;
+ }
+
+ void Clear() {
+ if (active_iterator_count_) {
+ for (size_t i = 0; i < callbacks_.size(); ++i)
+ callbacks_[i].Reset();
+ } else {
+ callbacks_.clear();
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ size_t size() const { return callbacks_.size(); }
+
+ private:
+ friend class CallbackListBase::Iterator;
+ typedef std::vector<SIG> ListType;
+ ListType callbacks_;
+ int active_iterator_count_;
+ 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) {
+ CallbackListBase<SIG>::Compact();
+ DCHECK_EQ(0U, CallbackListBase<SIG>::size());
+ }
+ }
+
+ 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(!(cb = it.GetNext()).is_null()) \
+ cb.Run(__VA_ARGS__); \
+ } \
+ } while (0)
+
+#endif // BASE_CALLBACK_LIST_H_
« no previous file with comments | « base/base.gypi ('k') | base/callback_list_unittest.cc » ('j') | base/callback_list_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698