1 /* bzflag
2 * Copyright (c) 1993-2021 Tim Riker
3 *
4 * This package is free software; you can redistribute it and/or
5 * modify it under the terms of the license found in the file
6 * named COPYING that should have accompanied this file.
7 *
8 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12
13 #ifndef BZF_CALLBACK_LIST_H
14 #define BZF_CALLBACK_LIST_H
15
16 // system headers
17 #include <utility>
18 #include <list>
19 #include <map>
20
21 // local implementation headers
22 #include "common.h"
23
24 template <class F>
25 class CallbackList
26 {
27 public:
28 typedef bool (*Callback)(F, void* userData, void* iterateUserData);
29
30 CallbackList();
31 ~CallbackList();
32
33 // add/remove callback. adding an existing callback has no effect.
34 void add(F func, void* userData);
35 void remove(F func, void* userData);
36
37 // iterate over callbacks. this is done by invoking the given
38 // callback function for each stored callback. it is safe to
39 // add and remove callbacks during iteration. stops iterating
40 // if the callback returns false.
41 void iterate(Callback, void* userData) const;
42
43 private:
44 void doIterate(Callback, void* userData);
45
46 private:
47 typedef std::pair<F, void*> Item;
48 typedef std::list<Item> ItemList;
49 typedef std::map<Item, typename ItemList::iterator> ItemMap;
50
51 ItemList items;
52 ItemMap itemMap;
53 };
54
55 //
56 // CallbackList
57 //
58
59 template <class F>
CallbackList()60 CallbackList<F>::CallbackList()
61 {
62 // do nothing
63 }
64
65 template <class F>
~CallbackList()66 CallbackList<F>::~CallbackList()
67 {
68 // do nothing
69 }
70
71 template <class F>
add(F callback,void * userData)72 void CallbackList<F>::add(F callback, void* userData)
73 {
74 Item item = std::make_pair(callback, userData);
75 if (itemMap.find(item) == itemMap.end())
76 {
77 typename ItemList::iterator index = items.insert(items.end(), item);
78 itemMap.insert(std::make_pair(item, index));
79 }
80 }
81
82 template <class F>
remove(F callback,void * userData)83 void CallbackList<F>::remove(F callback, void* userData)
84 {
85 Item item = std::make_pair(callback, userData);
86 typename ItemMap::iterator index = itemMap.find(item);
87 if (index != itemMap.end())
88 {
89 items.erase(index->second);
90 itemMap.erase(index);
91 }
92 }
93
94 template <class F>
iterate(Callback callback,void * userData)95 void CallbackList<F>::iterate(Callback callback,
96 void* userData) const
97 {
98 const_cast<CallbackList<F>*>(this)->doIterate(callback, userData);
99 }
100
101 template <class F>
doIterate(Callback callback,void * userData)102 void CallbackList<F>::doIterate(Callback callback,
103 void* userData)
104 {
105 // insert a dummy item into the list. this is our safe harbor
106 // in case the list is modified while we're iterating over it.
107 // the dummy item will remain no matter what other changes
108 // occur to the list. as we invoke each callback we move the
109 // dummy item forward.
110 Item dummyItem = std::make_pair((F)NULL, (void*)NULL);
111 typename ItemList::iterator dummyIndex = items.insert(items.begin(), dummyItem);
112
113 // now invoke each callback
114 typename ItemList::iterator index = dummyIndex;
115 for (; ++index != items.end(); index = dummyIndex)
116 {
117 // move dummy past the item we're about to invoke
118 items.splice(dummyIndex, items, index);
119
120 // invoke callback. skip dummy items (any item with a NULL function).
121 // stop if a callback returns false.
122 if (index->first != NULL)
123 if (!callback(index->first, index->second, userData))
124 break;
125 }
126
127 // now remove the dummy item
128 items.erase(dummyIndex);
129 }
130
131 #endif
132
133 // Local Variables: ***
134 // mode: C++ ***
135 // tab-width: 4 ***
136 // c-basic-offset: 4 ***
137 // indent-tabs-mode: nil ***
138 // End: ***
139 // ex: shiftwidth=4 tabstop=4
140