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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef BASE_CALLBACK_LIST_H__
6 #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.
7
8 #include <algorithm>
9 #include <limits>
10 #include <vector>
11
12 #include "base/basictypes.h"
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/memory/weak_ptr.h"
16
17 ///////////////////////////////////////////////////////////////////////////////
18 //
19 // OVERVIEW:
20 //
21 // A container for a list of callbacks. Unlike a normal STL vector or list,
22 // this container can be modified during iteration without invalidating the
23 // iterator. So, it safely handles the case of an callback removing itself
24 // or other callback from the list while callbacks are being run.
25 //
26 // TYPICAL USAGE:
27 //
28 // class MyWidget {
29 // public:
30 // ...
31 //
32 // typedef base::Callback<void(Foo*)> OnFooCallback;
33 //
34 // base::Closure RegisterCallback(const OnFooCallback& cb) {
35 // return callback_list_.RegisterCallback(cb);
36 // }
37 //
38 // 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.
39 // FOR_EACH_CALLBACK(OnFooCallback, callback_list_, foo);
40 // }
41 //
42 // private:
43 // CallbackList<OnFooCallback> callback_list_;
44 // };
45 //
46 //
47 // class MyWidgetListener {
48 // public:
49 //
50 // MyWidgetListener::MyWidgetListener(){
Avi (use Gerrit) 2013/08/22 16:25:42 Space between ) {.
Cait (Slow) 2013/08/23 16:33:44 Done.
51 // 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.
52 // base::Bind(&MyWidgetListener::OnFoo, this));
53 // }
54 //
55 // MyWidgetListener::~MyWidgetListener() {
56 // remove_foo_callback_.Run();
57 // }
58 //
59 // void OnFoo(Foo* foo) {
60 // // 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.
61 // }
62 //
63 // private:
64 // base::Closure remove_foo_callback_;
65 // };
66 //
67 ///////////////////////////////////////////////////////////////////////////////
68
69 template <class SIG>
70 class CallbackListBase {
71 public:
72 // An iterator class that can be used to access the list of callbacks. See
73 // also the FOR_EACH_CALLBACK macro defined below.
74 class Iterator {
75 public:
76 Iterator(CallbackListBase<SIG>& list)
77 : list_(list.GetWeakPtr()),
78 index_(0) {
79 ++list_->notify_depth_;
80 }
81
82 ~Iterator() {
83 if (list_.get() && --list_->notify_depth_ == 0)
84 list_->Compact();
85 }
86
87 SIG GetNext() {
88 if (!list_.get())
89 return SIG();
90 ListType& callbacks = list_->callbacks_;
91 return index_ < callbacks.size() ? callbacks[index_++] : SIG();
92 }
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...
93
94 bool HasNext() {
95 if (!list_.get())
96 return false;
97 return index_ < list_->callbacks_.size();
98 }
99
100 private:
101 base::WeakPtr<CallbackListBase<SIG> > list_;
102 size_t index_;
103 };
104
105 CallbackListBase() : notify_depth_(0),
106 weak_factory_(this) {}
107
108 // Add a callback to the list. A callback should not be added to
109 // the same list more than once. The returned closure (used to remove the
110 // callback from the list) is guaranteed to be safe to run.
111 base::Closure RegisterCallback(const SIG& cb) {
112 for (size_t i = 0; i < callbacks_.size(); i++) {
113 if (callbacks_[i].Equals(cb)) {
114 NOTREACHED() << "Callbacks can only be added once!";
115 return base::Bind(&base::DoNothing);
116 }
117 }
118 callbacks_.push_back(cb);
119 base::Closure dereg = base::Bind(
120 &CallbackListBase::RemoveCallback, GetWeakPtr(), cb);
121 return dereg;
122 }
123
124 // Remove an callback from the list if it is in the list.
125 void RemoveCallback(const SIG& cb) {
126 for (size_t i = 0; i < callbacks_.size(); i++) {
127 if (callbacks_[i].Equals(cb)) {
128 if (notify_depth_)
129 callbacks_[i].Reset();
130 else
131 callbacks_.erase(callbacks_.begin() + i);
132 return;
133 }
134 }
135 }
136
137 bool HasCallback(const SIG& cb) const {
138 for (size_t i = 0; i < callbacks_.size(); ++i) {
139 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.
140 return true;
141 }
142 return false;
143 }
144
145 void Clear() {
146 if (notify_depth_) {
147 for (size_t i = 0; i < callbacks_.size(); ++i)
148 callbacks_[i].Reset();
149 } else {
150 callbacks_.clear();
151 }
152 }
153
154 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
155
156 protected:
157 base::WeakPtr<CallbackListBase<SIG>> GetWeakPtr() {
158 return weak_factory_.GetWeakPtr();
159 }
160
161 void Compact() {
162 for (size_t i = 0; i < callbacks_.size(); i++) {
163 if (callbacks_[i].is_null()) {
164 callbacks_.erase(callbacks_.begin() + i);
165 }
166 }
167 }
168
169 private:
170 friend class CallbackListBase::Iterator;
171 typedef std::vector<SIG> ListType;
172 ListType callbacks_;
173 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.
174 base::WeakPtrFactory<CallbackListBase<SIG>> weak_factory_;
175
176 DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
177 };
178
179 template <class SIG, bool check_empty = false>
180 class CallbackList : public CallbackListBase<SIG> {
181 public:
182 CallbackList() {}
183
184 ~CallbackList() {
185 // When check_empty is true, assert that the list is empty on destruction.
186 if (check_empty) {
187 CallbackListBase<SIG>::Compact();
188 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.
189 }
190 }
191
192 bool might_have_callbacks() const {
193 return CallbackListBase<SIG>::size() != 0;
194 }
195 };
196
197 #define FOR_EACH_CALLBACK(CallbackType, callback_list, ...) \
198 do { \
199 if ((callback_list).might_have_callbacks()) { \
200 CallbackListBase<CallbackType>::Iterator it(callback_list); \
201 CallbackType cb; \
202 while(it.HasNext()) { \
203 if(!(cb = it.GetNext()).is_null()) \
204 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!
205 } \
206 } \
207 } while (0)
208
209 #endif // BASE_CALLBACK_LIST_H__
OLDNEW
« 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