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

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: Remove the macro, template on details type Created 7 years, 3 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(const Foo&)> OnFooCallback;
33 //
34 // base::Closure RegisterCallback(const OnFooCallback& cb) {
35 // return callback_list_.RegisterCallback(cb);
36 // }
37 //
38 // private:
39 // void NotifyFoo(const Foo& foo) {
40 // callback_list_.Run(foo);
41 // }
42 //
43 // CallbackList<Foo> 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(const Foo& foo) {
60 // // Do something.
61 // }
62 //
63 // private:
64 // base::Closure remove_foo_callback_;
65 // };
66 //
67 ///////////////////////////////////////////////////////////////////////////////
68 struct NoDetail {};
69
70 template <typename Detail>
71 class CallbackList {
72 public:
73 typedef base::Callback<void(const Detail&)> CallbackType;
74
75 // Enumeration of which callbacks are called.
76 enum NotificationType {
77 // Specifies that any callbacks added during notification are called.
78 // This is the default type if no type is provided to the constructor.
79 NOTIFY_ALL,
80
81 // Specifies that callbacks added while sending out notifications are not
82 // called.
83 NOTIFY_EXISTING_ONLY
84 };
85
86 CallbackList()
87 : active_iterator_count_(0),
88 type_(NOTIFY_ALL),
89 weak_factory_(this) {}
90
91 explicit CallbackList(NotificationType type)
92 : active_iterator_count_(0),
93 type_(type),
94 weak_factory_(this) {}
95
96 ~CallbackList() {
97 Compact();
98 DCHECK_EQ(0U, size());
99 }
100
101 // Add a callback to the list. A callback should not be added to
102 // the same list more than once. The returned closure (used to remove the
103 // callback from the list) is guaranteed to be safe to run.
104 base::Closure RegisterCallback(
105 const base::Callback<void(const Detail&)>& cb) {
106 DCHECK(!cb.is_null());
107 for (size_t i = 0; i < callbacks_.size(); i++) {
108 if (callbacks_[i].Equals(cb)) {
109 NOTREACHED() << "Callbacks can only be added once!";
110 return base::Bind(&base::DoNothing);
111 }
112 }
113 callbacks_.push_back(cb);
114 base::Closure removal_cb = base::Bind(
115 &CallbackList::RemoveCallback, GetWeakPtr(), cb);
116 return removal_cb;
117 }
118
119 // Remove an callback from the list if it is in the list.
120 void RemoveCallback(const base::Callback<void(const Detail&)>& cb) {
awong 2013/08/27 01:01:06 Does this and HasCallback need to be exposed as pu
121 for (size_t i = 0; i < callbacks_.size(); i++) {
122 if (callbacks_[i].Equals(cb)) {
123 if (active_iterator_count_)
124 callbacks_[i].Reset();
125 else
awong 2013/08/27 01:01:06 I think style guide says you're supposed to use br
Mark Mentovai 2013/08/27 02:08:21 awong wrote:
awong 2013/08/27 16:51:35 Thanks for looking it up. Even so, in this case th
126 callbacks_.erase(callbacks_.begin() + i);
127 return;
128 }
129 }
130 }
131
132 bool HasCallback(const base::Callback<void(const Detail&)>& cb) const {
133 for (size_t i = 0; i < callbacks_.size(); ++i) {
134 if (callbacks_[i].Equals(cb))
135 return true;
136 }
137 return false;
138 }
139
140 void Run(const Detail& details) {
141 if (might_have_callbacks()) {
142 RunImpl(details);
143 }
144 }
145
146 void Clear() {
147 if (active_iterator_count_) {
148 for (size_t i = 0; i < callbacks_.size(); ++i) {
149 callbacks_[i].Reset();
150 }
151 } else {
152 callbacks_.clear();
153 }
154 }
155
156 bool might_have_callbacks() const {
157 return size() != 0;
158 }
159
160 private:
161 typedef std::vector<base::Callback<void(const Detail&)> > ListType;
162
163 // An iterator class that can be used to access the list of callbacks.
164 class Iterator {
165 public:
166 Iterator(CallbackList<Detail>* list)
167 : list_(list->GetWeakPtr()),
168 index_(0),
169 max_index_(list->type_ == NOTIFY_ALL ?
170 std::numeric_limits<size_t>::max() :
171 list->callbacks_.size()) {
172 ++list_->active_iterator_count_;
173 }
174
175 ~Iterator() {
176 if (list_.get() && --list_->active_iterator_count_ == 0)
177 list_->Compact();
178 }
179
180 base::Callback<void(const Detail&)> GetNext() {
181 if (!list_.get())
182 return base::Callback<void(const Detail&)>();
183 ListType& callbacks = list_->callbacks_;
184 size_t max_index = std::min(max_index_, callbacks.size());
185 while (index_ < max_index && callbacks[index_].is_null())
186 ++index_;
187 return index_ < max_index ? callbacks[index_++] :
188 base::Callback<void(const Detail&)>();
189 }
190
191 private:
192 base::WeakPtr<CallbackList<Detail> > list_;
193 size_t index_;
194 size_t max_index_;
195 };
196
197 base::WeakPtr<CallbackList<Detail> > GetWeakPtr() {
198 return weak_factory_.GetWeakPtr();
199 }
200
201 void Compact() {
202 size_t i = 0;
203 while (i < callbacks_.size()) {
204 if (callbacks_[i].is_null())
205 callbacks_.erase(callbacks_.begin() + i);
206 else
207 ++i;
208 }
209 }
210
211 // TODO(caitkp): Is there a better way to do this, while still keeping
212 // Iterator priavte?
213 void RunImpl(const Detail& details) {
214 Iterator it(this);
215 CallbackType cb;
216 while(!(cb = it.GetNext()).is_null())
217 cb.Run(details);
218 }
219 size_t size() const { return callbacks_.size(); }
220
221 ListType callbacks_;
222 int active_iterator_count_;
223 NotificationType type_;
224 base::WeakPtrFactory<CallbackList<Detail> > weak_factory_;
225
226 DISALLOW_COPY_AND_ASSIGN(CallbackList);
227 };
228
229 // TODO(caitkp): Figure out how to share implementation so we don't have to
230 // rewrite the whole class in the specialization.
231 /*template <>
232 class CallbackList<NoDetail> {
awong 2013/08/27 01:01:06 Hmm...thinking back on the mess between scoped_ptr
233 public:
234 void Run() {
235 // Specialization for NoDetails has do Run method.
236 if (CallbackList<NoDetail>::might_have_callbacks())
237 RunImpl(NoDetail());
238 }
239 };
240 */
241
242 #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