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

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..a492179226887e0766eb36ab6f93d5bd522172f9
--- /dev/null
+++ b/base/callback_list.h
@@ -0,0 +1,209 @@
+// 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__
Avi (use Gerrit) 2013/08/22 16:25:42 One trailing _. (Yes, I see that ObserverList stil
Cait (Slow) 2013/08/23 16:33:44 Done.
+
+#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) {
Avi (use Gerrit) 2013/08/22 16:25:42 NotifyFoo really belongs in the private section; i
Cait (Slow) 2013/08/23 16:33:44 Done.
+// FOR_EACH_CALLBACK(OnFooCallback, callback_list_, foo);
+// }
+//
+// private:
+// CallbackList<OnFooCallback> callback_list_;
+// };
+//
+//
+// class MyWidgetListener {
+// public:
+//
+// MyWidgetListener::MyWidgetListener(){
Avi (use Gerrit) 2013/08/22 16:25:42 Space between ) {.
Cait (Slow) 2013/08/23 16:33:44 Done.
+// remove_foo_callback_ = MyWidget::GetCurrent()->RegisterCallback(
Avi (use Gerrit) 2013/08/22 16:25:42 Only one space between _ =.
Cait (Slow) 2013/08/23 16:33:44 Done.
+// base::Bind(&MyWidgetListener::OnFoo, this));
+// }
+//
+// MyWidgetListener::~MyWidgetListener() {
+// remove_foo_callback_.Run();
+// }
+//
+// void OnFoo(Foo* foo) {
+// // do something.
Avi (use Gerrit) 2013/08/22 16:25:42 Comments are complete sentences. Capitalizing "do"
Cait (Slow) 2013/08/23 16:33:44 Done.
+// }
+//
+// 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_->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();
+ }
Avi (use Gerrit) 2013/08/22 16:25:42 This is a very strange iterator class, one that is
Cait (Slow) 2013/08/23 16:33:44 So I've cleaned up the iterator class a bit. Notab
Avi (use Gerrit) 2013/08/23 16:51:11 What!? Macros aren't class-scoped? Yeah... ok...
+
+ 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 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) {
+ 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 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))
Avi (use Gerrit) 2013/08/22 16:25:42 Can you compile and run your unit test before uplo
Cait (Slow) 2013/08/23 16:33:44 Done.
+ 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(); }
Avi (use Gerrit) 2013/08/22 16:25:42 This is misleading, as it counts deleted items. I'
Cait (Slow) 2013/08/23 16:33:44 Done. (NB: Parallel observerList changes coming in
+
+ 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_;
Avi (use Gerrit) 2013/08/22 16:25:42 IMO this should be named "active_iterator_count_"
Cait (Slow) 2013/08/23 16:33:44 Done.
+ 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(CallbackListBase<SIG>::size(), 0U);
Avi (use Gerrit) 2013/08/22 16:25:42 In DCHECKs the constant is the first parameter. Ye
Cait (Slow) 2013/08/23 16:33:44 Done.
+ }
+ }
+
+ 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__ ) ; \
Avi (use Gerrit) 2013/08/22 16:25:42 Are those two odd spaces required? Eew.
Avi (use Gerrit) 2013/08/22 16:25:42 Variadic macros are new in C99. Does this work in
Cait (Slow) 2013/08/23 16:33:44 Done.
Cait (Slow) 2013/08/23 16:33:44 Good question -- to the trybots!
+ } \
+ } \
+ } 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