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

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, 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
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);
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>
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_->active_iterator_count_;
80 }
81
82 ~Iterator() {
83 if (list_.get() && --list_->active_iterator_count_ == 0)
84 list_->Compact();
85 }
86
87 SIG GetNext() {
88 if (!list_.get())
89 return SIG();
90 ListType& callbacks = list_->callbacks_;
91 while (index_ < callbacks.size() && callbacks[index_].is_null())
92 ++index_;
93 return index_ < callbacks.size() ? callbacks[index_++] : SIG();
94 }
95
96 private:
97 base::WeakPtr<CallbackListBase<SIG> > list_;
98 size_t index_;
99 };
100
101 CallbackListBase() : active_iterator_count_(0),
102 weak_factory_(this) {}
103
104 // Add a callback to the list. A callback should not be added to
105 // the same list more than once. The returned closure (used to remove the
106 // callback from the list) is guaranteed to be safe to run.
107 base::Closure RegisterCallback(const SIG& cb) {
108 if (cb.is_null())
109 return base::Bind(&base::DoNothing);
Avi (use Gerrit) 2013/08/23 16:51:12 Now that is defensive programming. I'm actually la
Cait (Slow) 2013/08/23 18:21:10 Done.
110
111 for (size_t i = 0; i < callbacks_.size(); i++) {
112 if (callbacks_[i].Equals(cb)) {
113 NOTREACHED() << "Callbacks can only be added once!";
114 return base::Bind(&base::DoNothing);
115 }
116 }
117 callbacks_.push_back(cb);
118 base::Closure removal_cb = base::Bind(
119 &CallbackListBase::RemoveCallback, GetWeakPtr(), cb);
120 return removal_cb;
121 }
122
123 // Remove an callback from the list if it is in the list.
124 void RemoveCallback(const SIG& cb) {
125 for (size_t i = 0; i < callbacks_.size(); i++) {
126 if (callbacks_[i].Equals(cb)) {
127 if (active_iterator_count_)
128 callbacks_[i].Reset();
129 else
130 callbacks_.erase(callbacks_.begin() + i);
131 return;
132 }
133 }
134 }
135
136 bool HasCallback(const SIG& cb) const {
137 for (size_t i = 0; i < callbacks_.size(); ++i) {
138 if (callbacks_[i].Equals(cb))
139 return true;
140 }
141 return false;
142 }
143
144 void Clear() {
145 if (active_iterator_count_) {
146 for (size_t i = 0; i < callbacks_.size(); ++i)
147 callbacks_[i].Reset();
148 } else {
149 callbacks_.clear();
150 }
151 }
152
153 protected:
154 base::WeakPtr<CallbackListBase<SIG> > GetWeakPtr() {
155 return weak_factory_.GetWeakPtr();
156 }
157
158 void Compact() {
159 for (size_t i = 0; i < callbacks_.size(); i++) {
160 if (callbacks_[i].is_null()) {
161 callbacks_.erase(callbacks_.begin() + i);
162 }
163 }
164 }
165
166 size_t size() const { return callbacks_.size(); }
167
168 private:
169 friend class CallbackListBase::Iterator;
170 typedef std::vector<SIG> ListType;
171 ListType callbacks_;
172 int active_iterator_count_;
173 base::WeakPtrFactory<CallbackListBase<SIG> > weak_factory_;
174
175 DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
176 };
177
178 template <class SIG, bool check_empty = false>
179 class CallbackList : public CallbackListBase<SIG> {
180 public:
181 CallbackList() {}
182
183 ~CallbackList() {
184 // When check_empty is true, assert that the list is empty on destruction.
185 if (check_empty) {
186 CallbackListBase<SIG>::Compact();
187 DCHECK_EQ(0U, CallbackListBase<SIG>::size());
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(!(cb = it.GetNext()).is_null()) \
202 cb.Run(__VA_ARGS__); \
203 } \
204 } while (0)
205
206 #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