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

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 (c) 2011 The Chromium Authors. All rights reserved.
Avi (use Gerrit) 2013/08/21 19:56:18 No (c), 2013. For all new files.
Cait (Slow) 2013/08/22 15:30:25 Done.
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 // void NotifyFoo(Foo* foo) {
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(){
51 // remove_foo_callback_ = MyWidget::GetCurrent()->RegisterCallback(
52 // base::Bind(&MyWidgetListener::OnFoo, this));
53 // }
54 //
55 // MyWidgetListener::~MyWidgetListener() {
56 // if (!remove_foo_callback_.is_null())
57 // remove_foo_callback_.Run();
Avi (use Gerrit) 2013/08/21 19:56:18 Grr. Not a fan of is_null. Drop it; I have a sugge
Cait (Slow) 2013/08/22 15:30:25 Done -- there are a couple more uses of is_null(),
Avi (use Gerrit) 2013/08/22 16:25:42 I don't mind internal use of is_null. But when you
58 // }
59 //
60 // void OnFoo(Foo* foo) {
61 // // do something.
62 // }
63 //
64 // private:
65 // base::Closure remove_foo_callback_;
66 // };
67 //
68 ///////////////////////////////////////////////////////////////////////////////
69
70 template <class SIG>
Avi (use Gerrit) 2013/08/21 19:56:18 no indent
Cait (Slow) 2013/08/22 15:30:25 Done.
71 class CallbackListBase {
72 public:
73 // An iterator class that can be used to access the list of callbacks. See
74 // also the FOR_EACH_CALLBACK macro defined below.
75 class Iterator {
76 public:
77 Iterator(CallbackListBase<SIG>& list)
78 : list_(list.GetWeakPtr()),
79 index_(0) {
80 ++list_->notify_depth_;
81 }
82
83 ~Iterator() {
84 if (list_.get() && --list_->notify_depth_ == 0)
85 list_->Compact();
86 }
87
88 SIG GetNext() {
89 if (!list_.get())
90 return SIG();
91 ListType& callbacks = list_->callbacks_;
92 return index_ < callbacks.size() ? callbacks[index_++] : SIG();
93 }
94
95 bool HasNext() {
96 if (!list_.get())
97 return false;
98 return index_ < list_->callbacks_.size();
99 }
100
101 private:
102 base::WeakPtr<CallbackListBase<SIG> > list_;
103 size_t index_;
104 };
105
106 CallbackListBase() : notify_depth_(0),
107 weak_factory_(this) {}
108
109 // Add an callback to the list. An callback should not be added to
110 // the same list more than once.
Avi (use Gerrit) 2013/08/21 19:56:18 Guarantee in the comment that you can always safel
Cait (Slow) 2013/08/22 15:30:25 Done.
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::Closure();
Avi (use Gerrit) 2013/08/21 19:56:18 ... and rather than returning base::Closure(), ret
Cait (Slow) 2013/08/22 15:30:25 Done.
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))
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(); }
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_;
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 DCHECK_EQ(CallbackListBase<SIG>::size(), 0U);
188 }
189 }
190
191 bool might_have_callbacks() const {
192 return CallbackListBase<SIG>::size() != 0;
193 }
194 };
195
196 #define FOR_EACH_CALLBACK(CallbackType, callback_list, ...) \
197 do { \
198 if ((callback_list).might_have_callbacks()) { \
199 CallbackListBase<CallbackType>::Iterator it(callback_list); \
200 CallbackType cb; \
201 while(it.HasNext()) { \
202 if(!(cb = it.GetNext()).is_null()) \
203 cb.Run(__VA_ARGS__ ) ; \
204 } \
205 } \
206 } while (0)
207
208 #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