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