- コールバック前にnumListenersを取得するのでAdd直後に通知が来ない
- コールバック中にindexがずれないように、RemoveはNULLで置き換えるだけにしてコールバック後に一括消去
- vector::iteratorはpush_backしたときに無効化されることがあるのでindexを使ってループする
ListenerList.h
#ifndef LISTENERLIST_H_
#define LISTENERLIST_H_
#include <vector>
#include <boost/function.hpp>
#include <boost/thread/mutex.hpp>
template< class Listener >
class ListenerList
{
public:
inline ListenerList() : mNumActiveNotifications(0), mNeedCleanup(false) {}
inline void Add(Listener* listener)
{
boost::mutex::scoped_lock listenerLock(mListenerMutex);
typename std::vector<Listener*>::iterator it = std::find(mListeners.begin(), mListeners.end(), listener);
if (it != mListeners.end()) return;
mListeners.push_back(listener);
}
inline void Remove(Listener* listener)
{
boost::mutex::scoped_lock listenerLock(mListenerMutex);
typename std::vector<Listener*>::iterator it = std::find(mListeners.begin(), mListeners.end(), listener);
if (it == mListeners.end()) return;
if (mNumActiveNotifications > 0)
{
*it = NULL;
mNeedCleanup = true;
}
else
{
mListeners.erase(it);
}
}
inline void Notify(const boost::function<void (Listener*)> callback)
{
boost::mutex::scoped_lock listenerLock(mListenerMutex);
++mNumActiveNotifications;
const size_t numListeners = mListeners.size();
for (size_t i = 0; i < numListeners; ++i)
{
Listener* listener = mListeners[i];
if (listener != NULL)
{
listenerLock.unlock();
callback(listener);
listenerLock.lock();
}
}
--mNumActiveNotifications;
if (mNumActiveNotifications == 0 && mNeedCleanup)
{
mListeners.erase(std::remove(mListeners.begin(), mListeners.end(), (Listener*)NULL),
mListeners.end());
mNeedCleanup = false;
}
}
/*
inline size_t Size()
{
boost::mutex::scoped_lock listenerLock(mListenerMutex);
return mListeners.size();
}
inline Listener* operator[](size_t index)
{
boost::mutex::scoped_lock listenerLock(mListenerMutex);
if (index >= mListeners.size()) return NULL;
return mListeners[index];
}
*/
private:
std::vector<Listener*> mListeners;
int mNumActiveNotifications;
bool mNeedCleanup;
boost::mutex mListenerMutex;
};
#endif /* LISTENERLIST_H_ */