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

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
« no previous file with comments | « base/base.gypi ('k') | base/callback_list_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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_
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 // private:
39 // void NotifyFoo(Foo* foo) {
40 // FOR_EACH_CALLBACK(OnFooCallback, callback_list_, foo);
awong 2013/08/24 04:11:07 Is there a way to make this into something like c
Cait (Slow) 2013/08/26 23:49:27 Done.
41 // }
42 //
43 // CallbackList<OnFooCallback> callback_list_;
44 // };
45 //
46 //
47 // class MyWidgetListener {
48 // public:
49 //
50 // MyWidgetListener::MyWidgetListener() {
51 // remove_foo_callback_ = MyWidget::GetCurrent()->RegisterCallback(
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.
61 // }
62 //
63 // private:
64 // base::Closure remove_foo_callback_;
65 // };
66 //
67 ///////////////////////////////////////////////////////////////////////////////
68
69 template <class SIG>
awong 2013/08/24 04:11:07 nit: The convention in base/callback.h is to use "
Cait (Slow) 2013/08/26 23:49:27 Done.
70 class CallbackListBase {
71 public:
72 // Enumeration of which callbacks are called.
73 enum NotificationType {
74 // Specifies that any callbacks added during notification are called.
75 // This is the default type if no type is provided to the constructor.
76 NOTIFY_ALL,
77
78 // Specifies that callbacks added while sending out notifications are not
79 // called.
80 NOTIFY_EXISTING_ONLY
81 };
82
83 // An iterator class that can be used to access the list of callbacks. See
84 // also the FOR_EACH_CALLBACK macro defined below.
85 class Iterator {
86 public:
87 Iterator(CallbackListBase<SIG>& list)
awong 2013/08/24 04:11:07 Taking things by reference is disallowed by style
Cait (Slow) 2013/08/26 23:49:27 Done.
88 : list_(list.GetWeakPtr()),
89 index_(0),
90 max_index_(list.type_ == NOTIFY_ALL ?
91 std::numeric_limits<size_t>::max() :
92 list.callbacks_.size()) {
93 ++list_->active_iterator_count_;
94 }
95
96 ~Iterator() {
97 if (list_.get() && --list_->active_iterator_count_ == 0)
98 list_->Compact();
99 }
100
101 SIG GetNext() {
102 if (!list_.get())
103 return SIG();
104 ListType& callbacks = list_->callbacks_;
105 size_t max_index = std::min(max_index_, callbacks.size());
106 while (index_ < max_index && callbacks[index_].is_null())
107 ++index_;
108 return index_ < max_index ? callbacks[index_++] : SIG();
109 }
110
111 private:
112 base::WeakPtr<CallbackListBase<SIG> > list_;
113 size_t index_;
114 size_t max_index_;
115 };
116
117 CallbackListBase()
118 : active_iterator_count_(0),
119 type_(NOTIFY_ALL),
120 weak_factory_(this) {}
121
122 explicit CallbackListBase(NotificationType type)
123 : active_iterator_count_(0),
124 type_(type),
125 weak_factory_(this) {}
126
127 // Add a callback to the list. A callback should not be added to
128 // the same list more than once. The returned closure (used to remove the
129 // callback from the list) is guaranteed to be safe to run.
130 base::Closure RegisterCallback(const SIG& cb) {
131 DCHECK(!cb.is_null());
132 for (size_t i = 0; i < callbacks_.size(); i++) {
133 if (callbacks_[i].Equals(cb)) {
134 NOTREACHED() << "Callbacks can only be added once!";
135 return base::Bind(&base::DoNothing);
136 }
137 }
138 callbacks_.push_back(cb);
139 base::Closure removal_cb = base::Bind(
140 &CallbackListBase::RemoveCallback, GetWeakPtr(), cb);
141 return removal_cb;
142 }
143
144 // Remove an callback from the list if it is in the list.
145 void RemoveCallback(const SIG& cb) {
146 for (size_t i = 0; i < callbacks_.size(); i++) {
147 if (callbacks_[i].Equals(cb)) {
148 if (active_iterator_count_)
149 callbacks_[i].Reset();
150 else
151 callbacks_.erase(callbacks_.begin() + i);
152 return;
153 }
154 }
155 }
156
157 bool HasCallback(const SIG& cb) const {
158 for (size_t i = 0; i < callbacks_.size(); ++i) {
159 if (callbacks_[i].Equals(cb))
160 return true;
161 }
162 return false;
163 }
164
165 void Clear() {
166 if (active_iterator_count_) {
167 for (size_t i = 0; i < callbacks_.size(); ++i)
168 callbacks_[i].Reset();
169 } else {
170 callbacks_.clear();
171 }
172 }
173
174 protected:
175 base::WeakPtr<CallbackListBase<SIG> > GetWeakPtr() {
176 return weak_factory_.GetWeakPtr();
177 }
178
179 void Compact() {
180 for (size_t i = 0; i < callbacks_.size(); i++) {
181 if (callbacks_[i].is_null()) {
182 callbacks_.erase(callbacks_.begin() + i);
183 }
184 }
185 }
186
187 size_t size() const { return callbacks_.size(); }
188
189 private:
190 friend class CallbackListBase::Iterator;
191 typedef std::vector<SIG> ListType;
192
193 ListType callbacks_;
194 int active_iterator_count_;
195 NotificationType type_;
196 base::WeakPtrFactory<CallbackListBase<SIG> > weak_factory_;
197
198 DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
199 };
200
201 template <class SIG, bool check_empty = false>
202 class CallbackList : public CallbackListBase<SIG> {
203 public:
204 typedef typename CallbackListBase<SIG>::NotificationType NotificationType;
205
206 CallbackList() {}
207 explicit CallbackList(NotificationType type)
208 : CallbackListBase<SIG>(type) {}
209
210 ~CallbackList() {
211 // When check_empty is true, assert that the list is empty on destruction.
212 if (check_empty) {
awong 2013/08/24 04:11:07 If you're taking this as a template constant param
Cait (Slow) 2013/08/26 23:49:27 Done.
213 CallbackListBase<SIG>::Compact();
214 DCHECK_EQ(0U, CallbackListBase<SIG>::size());
215 }
216 }
217
218 bool might_have_callbacks() const {
219 return CallbackListBase<SIG>::size() != 0;
220 }
221 };
222
223 #define FOR_EACH_CALLBACK(CallbackType, callback_list, ...) \
224 do { \
225 if ((callback_list).might_have_callbacks()) { \
226 CallbackListBase<CallbackType>::Iterator it(callback_list); \
227 CallbackType cb; \
228 while(!(cb = it.GetNext()).is_null()) \
229 cb.Run(__VA_ARGS__); \
230 } \
231 } while (0)
232
233 #endif // BASE_CALLBACK_LIST_H_
OLDNEW
« no previous file with comments | « base/base.gypi ('k') | base/callback_list_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698