Hey, hatte mir als alternative zu boost::signal (da ich die irgendwie unbequem finde) eine eigene Event Klasse zusammengebaut.
Das ganze setzt voraus, dass euer Compiler variadische Templates unterstützt, ansonsten könnt ihr die Klasse mit mehreren Template Spezialisierungen überladen (hab da momentan 4 Überladungen drin, mehr Argumente brauch ich eigentlich nie).
Naja sieht jedenfalls so aus:
Anwendungsbeispiel:
Benutze zum registrieren der Events noch meinen TokenManager, der sieht einfach so aus:
Das ganze setzt voraus, dass euer Compiler variadische Templates unterstützt, ansonsten könnt ihr die Klasse mit mehreren Template Spezialisierungen überladen (hab da momentan 4 Überladungen drin, mehr Argumente brauch ich eigentlich nie).
Naja sieht jedenfalls so aus:
Code:
namespace Utils
{
struct Minimum {
template <typename T, typename InputIterator>
static T Comp(InputIterator first, InputIterator last) {
if (first == last)
return T();
T min_value = *first++;
while (first != last) {
if (min_value > *first)
min_value = *first;
++first;
}
return min_value;
}
};
struct Maximum {
template <typename T, typename InputIterator>
static T Comp(InputIterator first, InputIterator last) {
if (first == last)
return T();
T max_value = *first++;
while (first != last) {
if (max_value < *first)
max_value = *first;
++first;
}
return max_value;
}
};
template <typename FuncT>
struct SEventDelegate {
uint64 token;
std::function<FuncT> callback;
};
template <typename FuncT>
class Event {};
#ifdef VARIADIC_TEMPLATES_SUPPORTED
// Threadsafe
template <typename R, typename... Args>
class Event<R (Args...)>
{
public:
typedef R fnconv_t(Args...);
Event() : m_mutex(), m_tokenMgr(), m_delegates() {}
Event(const std::function<fnconv_t> &func) :
m_mutex(), m_tokenMgr(), m_delegates() {
connect(func);
}
Event(Event<fnconv_t> &&other) :
m_tokenMgr(std::move(other.m_tokenMgr)),
m_mutex(std::move(other.m_mutex)),
m_delegates(std::move(other.m_delegates)) {}
Event<fnconv_t>& operator=(Event<fnconv_t> &&other) {
m_tokenMgr = std::move(other.m_tokenMgr);
m_mutex = std::move(other.m_mutex);
m_delegates = std::move(other.m_delegates);
return *this;
}
R operator()(const Args&... args) const {
return _invoke_impl<R, Maximum>(args...);
}
template <typename CompT>
R invoke(const Args&... args) const {
return _invoke_impl<R, CompT>(args...);
}
SEventDelegate<fnconv_t> operator+=(const std::function<fnconv_t> &func) {
return connect(func);
}
Event<fnconv_t>& operator-=(const SEventDelegate<fnconv_t> &deleg) {
remove(deleg.token);
return *this;
}
SEventDelegate<fnconv_t> connect(const std::function<fnconv_t> &func) {
std::lock_guard<std::mutex> g(m_mutex);
SEventDelegate<fnconv_t> deleg = { m_tokenMgr.get(), func };
m_delegates[deleg.token] = deleg;
return deleg;
}
void remove(const uint64 &token) {
std::lock_guard<std::mutex> g(m_mutex);
auto itr = m_delegates.find(token);
if (itr != m_delegates.end()) {
m_tokenMgr.remove(token);
m_delegates.erase(itr);
}
}
void clear() {
m_mutex.lock();
m_delegates.clear();
m_tokenMgr.reset();
m_mutex.unlock();
}
protected:
TokenManager m_tokenMgr;
mutable std::mutex m_mutex;
std::map<uint64, SEventDelegate<fnconv_t>> m_delegates;
template <typename T, typename CompT>
typename std::enable_if<std::is_same<void, T>::value, T>::type
_invoke_impl(const Args&... args) const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
for (auto& pair: mDelegates)
pair.second.callback(args...);
}
template <typename T, typename CompT>
typename std::enable_if<!std::is_same<void, T>::value, T>::type
_invoke_impl(const Args&... args) const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
std::vector<T> results;
results.reserve(mDelegates.size());
for (auto& pair: mDelegates)
results.push_back(pair.second.callback(args...));
return CompT::Comp<T>(results.cbegin(), results.cend());
}
};
#else
// [...] kein Variadic Template Support
// Overloads von partiellen Template-Spezialisierungen benutzen.
//////////// EVENT P0 //////////////
// Threadsafe
template <typename R>
class Event<R (void)>
{
public:
typedef R fnconv_t(void);
Event() : m_mutex(), m_tokenMgr(), m_delegates() {}
Event(const std::function<fnconv_t> &func) :
m_mutex(), m_tokenMgr(), m_delegates() {
connect(func);
}
Event(Event<fnconv_t> &&other) :
m_tokenMgr(std::move(other.m_tokenMgr)),
m_mutex(std::move(other.m_mutex)),
m_delegates(std::move(other.m_delegates)) {}
Event<fnconv_t>& operator=(Event<fnconv_t> &&other) {
m_tokenMgr = std::move(other.m_tokenMgr);
m_mutex = std::move(other.m_mutex);
m_delegates = std::move(other.m_delegates);
return *this;
}
R operator()() const {
return _invoke_impl<R, Maximum>();
}
template <typename CompT>
R invoke() const {
return _invoke_impl<R, CompT>();
}
SEventDelegate<fnconv_t> operator+=(const std::function<fnconv_t> &func) {
return connect(func);
}
Event<fnconv_t>& operator-=(const SEventDelegate<fnconv_t> &deleg) {
remove(deleg.token);
return *this;
}
SEventDelegate<fnconv_t> connect(const std::function<fnconv_t> &func) {
std::lock_guard<std::mutex> g(m_mutex);
SEventDelegate<fnconv_t> deleg = { m_tokenMgr.get(), func };
m_delegates[deleg.token] = deleg;
return deleg;
}
void remove(const uint64 &token) {
std::lock_guard<std::mutex> g(m_mutex);
auto itr = m_delegates.find(token);
if (itr != m_delegates.end()) {
m_tokenMgr.remove(token);
m_delegates.erase(itr);
}
}
void clear() {
m_mutex.lock();
m_delegates.clear();
m_tokenMgr.reset();
m_mutex.unlock();
}
protected:
TokenManager m_tokenMgr;
mutable std::mutex m_mutex;
std::map<uint64, SEventDelegate<fnconv_t>> m_delegates;
template <typename T, typename CompT>
typename std::enable_if<std::is_same<void, T>::value, T>::type
_invoke_impl() const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
for (auto& pair: mDelegates)
pair.second.callback();
}
template <typename T, typename CompT>
typename std::enable_if<!std::is_same<void, T>::value, T>::type
_invoke_impl() const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
std::vector<T> results;
results.reserve(mDelegates.size());
for (auto& pair: mDelegates)
results.push_back(pair.second.callback());
return CompT::Comp<T>(results.cbegin(), results.cend());
}
};
//////////// EVENT P1 //////////////
// Threadsafe
template <typename R, typename A0>
class Event<R (A0)>
{
public:
typedef R fnconv_t(A0);
Event() : m_mutex(), m_tokenMgr(), m_delegates() {}
Event(const std::function<fnconv_t> &func) :
m_mutex(), m_tokenMgr(), m_delegates() {
connect(func);
}
Event(Event<fnconv_t> &&other) :
m_tokenMgr(std::move(other.m_tokenMgr)),
m_mutex(std::move(other.m_mutex)),
m_delegates(std::move(other.m_delegates)) {}
Event<fnconv_t>& operator=(Event<fnconv_t> &&other) {
m_tokenMgr = std::move(other.m_tokenMgr);
m_mutex = std::move(other.m_mutex);
m_delegates = std::move(other.m_delegates);
return *this;
}
R operator()(const A0 &a0) const {
return _invoke_impl<R, Maximum>(a0);
}
template <typename CompT>
R invoke(const A0 &a0) const {
return _invoke_impl<R, CompT>(a0);
}
SEventDelegate<fnconv_t> operator+=(const std::function<fnconv_t> &func) {
return connect(func);
}
Event<fnconv_t>& operator-=(const SEventDelegate<fnconv_t> &deleg) {
remove(deleg.token);
return *this;
}
SEventDelegate<fnconv_t> connect(const std::function<fnconv_t> &func) {
std::lock_guard<std::mutex> g(m_mutex);
SEventDelegate<fnconv_t> deleg = { m_tokenMgr.get(), func };
m_delegates[deleg.token] = deleg;
return deleg;
}
void remove(const uint64 &token) {
std::lock_guard<std::mutex> g(m_mutex);
auto itr = m_delegates.find(token);
if (itr != m_delegates.end()) {
m_tokenMgr.remove(token);
m_delegates.erase(itr);
}
}
void clear() {
m_mutex.lock();
m_delegates.clear();
m_tokenMgr.reset();
m_mutex.unlock();
}
protected:
TokenManager m_tokenMgr;
mutable std::mutex m_mutex;
std::map<uint64, SEventDelegate<fnconv_t>> m_delegates;
template <typename T, typename CompT>
typename std::enable_if<std::is_same<void, T>::value, T>::type
_invoke_impl(const A0 &a0) const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
for (auto& pair: mDelegates)
pair.second.callback(a0);
}
template <typename T, typename CompT>
typename std::enable_if<!std::is_same<void, T>::value, T>::type
_invoke_impl(const A0 &a0) const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
std::vector<T> results;
results.reserve(mDelegates.size());
for (auto& pair: mDelegates)
results.push_back(pair.second.callback(a0));
return CompT::Comp<T>(results.cbegin(), results.cend());
}
};
//////////// EVENT P2 //////////////
// Threadsafe
template <typename R, typename A0, typename A1>
class Event<R (A0, A1)>
{
public:
typedef R fnconv_t(A0, A1);
Event() : m_mutex(), m_tokenMgr(), m_delegates() {}
Event(const std::function<fnconv_t> &func) :
m_mutex(), m_tokenMgr(), m_delegates() {
connect(func);
}
Event(Event<fnconv_t> &&other) :
m_tokenMgr(std::move(other.m_tokenMgr)),
m_mutex(std::move(other.m_mutex)),
m_delegates(std::move(other.m_delegates)) {}
Event<fnconv_t>& operator=(Event<fnconv_t> &&other) {
m_tokenMgr = std::move(other.m_tokenMgr);
m_mutex = std::move(other.m_mutex);
m_delegates = std::move(other.m_delegates);
return *this;
}
R operator()(const A0 &a0, const A1 &a1) const {
return _invoke_impl<R, Maximum>(a0, a1);
}
template <typename CompT>
R invoke(const A0 &a0, const A1 &a1) const {
return _invoke_impl<R, CompT>(a0, a1);
}
SEventDelegate<fnconv_t> operator+=(const std::function<fnconv_t> &func) {
return connect(func);
}
Event<fnconv_t>& operator-=(const SEventDelegate<fnconv_t> &deleg) {
remove(deleg.token);
return *this;
}
SEventDelegate<fnconv_t> connect(const std::function<fnconv_t> &func) {
std::lock_guard<std::mutex> g(m_mutex);
SEventDelegate<fnconv_t> deleg = { m_tokenMgr.get(), func };
m_delegates[deleg.token] = deleg;
return deleg;
}
void remove(const uint64 &token) {
std::lock_guard<std::mutex> g(m_mutex);
auto itr = m_delegates.find(token);
if (itr != m_delegates.end()) {
m_tokenMgr.remove(token);
m_delegates.erase(itr);
}
}
void clear() {
m_mutex.lock();
m_delegates.clear();
m_tokenMgr.reset();
m_mutex.unlock();
}
protected:
TokenManager m_tokenMgr;
mutable std::mutex m_mutex;
std::map<uint64, SEventDelegate<fnconv_t>> m_delegates;
template <typename T, typename CompT>
typename std::enable_if<std::is_same<void, T>::value, T>::type
_invoke_impl(const A0 &a0, const A1 &a1) const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
for (auto& pair: mDelegates)
pair.second.callback(a0, a1);
}
template <typename T, typename CompT>
typename std::enable_if<!std::is_same<void, T>::value, T>::type
_invoke_impl(const A0 &a0, const A1 &a1) const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
std::vector<T> results;
results.reserve(mDelegates.size());
for (auto& pair: mDelegates)
results.push_back(pair.second.callback(a0, a1));
return CompT::Comp<T>(results.cbegin(), results.cend());
}
};
//////////// EVENT P3 //////////////
// Threadsafe
template <typename R, typename A0, typename A1, typename A2>
class Event<R (A0, A1, A2)>
{
public:
typedef R fnconv_t(A0, A1, A2);
Event() : m_mutex(), m_tokenMgr(), m_delegates() {}
Event(const std::function<fnconv_t> &func) :
m_mutex(), m_tokenMgr(), m_delegates() {
connect(func);
}
Event(Event<fnconv_t> &&other) :
m_tokenMgr(std::move(other.m_tokenMgr)),
m_mutex(std::move(other.m_mutex)),
m_delegates(std::move(other.m_delegates)) {}
Event<fnconv_t>& operator=(Event<fnconv_t> &&other) {
m_tokenMgr = std::move(other.m_tokenMgr);
m_mutex = std::move(other.m_mutex);
m_delegates = std::move(other.m_delegates);
return *this;
}
R operator()(const A0 &a0, const A1 &a1, const A2 &a2) const {
return _invoke_impl<R, Maximum>(a0, a1, a2);
}
template <typename CompT>
R invoke(const A0 &a0, const A1 &a1, const A2 &a2) const {
return _invoke_impl<R, CompT>(a0, a1, a2);
}
SEventDelegate<fnconv_t> operator+=(const std::function<fnconv_t> &func) {
return connect(func);
}
Event<fnconv_t>& operator-=(const SEventDelegate<fnconv_t> &deleg) {
remove(deleg.token);
return *this;
}
SEventDelegate<fnconv_t> connect(const std::function<fnconv_t> &func) {
std::lock_guard<std::mutex> g(m_mutex);
SEventDelegate<fnconv_t> deleg = { m_tokenMgr.get(), func };
m_delegates[deleg.token] = deleg;
return deleg;
}
void remove(const uint64 &token) {
std::lock_guard<std::mutex> g(m_mutex);
auto itr = m_delegates.find(token);
if (itr != m_delegates.end()) {
m_tokenMgr.remove(token);
m_delegates.erase(itr);
}
}
void clear() {
m_mutex.lock();
m_delegates.clear();
m_tokenMgr.reset();
m_mutex.unlock();
}
protected:
TokenManager m_tokenMgr;
mutable std::mutex m_mutex;
std::map<uint64, SEventDelegate<fnconv_t>> m_delegates;
template <typename T, typename CompT>
typename std::enable_if<std::is_same<void, T>::value, T>::type
_invoke_impl(const A0 &a0, const A1 &a1, const A2 &a2) const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
for (auto& pair: mDelegates)
pair.second.callback(a0, a1, a2);
}
template <typename T, typename CompT>
typename std::enable_if<!std::is_same<void, T>::value, T>::type
_invoke_impl(const A0 &a0, const A1 &a1, const A2 &a2) const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
std::vector<T> results;
results.reserve(mDelegates.size());
for (auto& pair: mDelegates)
results.push_back(pair.second.callback(a0, a1, a2));
return CompT::Comp<T>(results.cbegin(), results.cend());
}
};
//////////// EVENT P4 //////////////
// Threadsafe
template <typename R, typename A0, typename A1, typename A2, typename A3>
class Event<R (A0, A1, A2, A3)>
{
public:
typedef R fnconv_t(A0, A1, A2, A3);
Event() : m_mutex(), m_tokenMgr(), m_delegates() {}
Event(const std::function<fnconv_t> &func) :
m_mutex(), m_tokenMgr(), m_delegates() {
connect(func);
}
Event(Event<fnconv_t> &&other) :
m_tokenMgr(std::move(other.m_tokenMgr)),
m_mutex(std::move(other.m_mutex)),
m_delegates(std::move(other.m_delegates)) {}
Event<fnconv_t>& operator=(Event<fnconv_t> &&other) {
m_tokenMgr = std::move(other.m_tokenMgr);
m_mutex = std::move(other.m_mutex);
m_delegates = std::move(other.m_delegates);
return *this;
}
R operator()(const A0 &a0, const A1 &a1, const A2 &a2, const A3 &a3) const {
return _invoke_impl<R, Maximum>(a0, a1, a2, a3);
}
template <typename CompT>
R invoke(const A0 &a0, const A1 &a1, const A2 &a2, const A3 &a3) const {
return _invoke_impl<R, CompT>(a0, a1, a2, a3);
}
SEventDelegate<fnconv_t> operator+=(const std::function<fnconv_t> &func) {
return connect(func);
}
Event<fnconv_t>& operator-=(const SEventDelegate<fnconv_t> &deleg) {
remove(deleg.token);
return *this;
}
SEventDelegate<fnconv_t> connect(const std::function<fnconv_t> &func) {
std::lock_guard<std::mutex> g(m_mutex);
SEventDelegate<fnconv_t> deleg = { m_tokenMgr.get(), func };
m_delegates[deleg.token] = deleg;
return deleg;
}
void remove(const uint64 &token) {
std::lock_guard<std::mutex> g(m_mutex);
auto itr = m_delegates.find(token);
if (itr != m_delegates.end()) {
m_tokenMgr.remove(token);
m_delegates.erase(itr);
}
}
void clear() {
m_mutex.lock();
m_delegates.clear();
m_tokenMgr.reset();
m_mutex.unlock();
}
protected:
TokenManager m_tokenMgr;
mutable std::mutex m_mutex;
std::map<uint64, SEventDelegate<fnconv_t>> m_delegates;
template <typename T, typename CompT>
typename std::enable_if<std::is_same<void, T>::value, T>::type
_invoke_impl(const A0 &a0, const A1 &a1, const A2 &a2, const A3 &a3) const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
for (auto& pair: mDelegates)
pair.second.callback(a0, a1, a2, a3);
}
template <typename T, typename CompT>
typename std::enable_if<!std::is_same<void, T>::value, T>::type
_invoke_impl(const A0 &a0, const A1 &a1, const A2 &a2, const A3 &a3) const {
m_mutex.lock();
auto mDelegates = m_delegates;
m_mutex.unlock();
std::vector<T> results;
results.reserve(mDelegates.size());
for (auto& pair: mDelegates)
results.push_back(pair.second.callback(a0, a1, a2, a3));
return CompT::Comp<T>(results.cbegin(), results.cend());
}
};
#endif
}
Code:
// Event erstellen...
Utils::Event<uint32 (uint32, uint32)> someEvent;
// Multiply Event registrieren...
auto mulDelegate = someEvent +=
[] (uint32 a, uint32 b) {
return a * b;
};
// Add-Event registrieren...
auto addDelegate = someEvent +=
[] (uint32 a, uint32 b) {
return a + b;
};
// Event mit Standard-Comparer (Utils::Maximum) aufrufen...
std::cout << someEvent(3, 5) << std::endl;
// Event mit explizitem Comparer (Utils::Minimum) aufrufen...
std::cout << someEvent.invoke<Utils::Minimum>(3, 5) << std::endl;
// Multiply-Event löschen...
someEvent -= mulDelegate;
// Nochmals mit Standard-Comparer (Utils::Maximum) aufrufen...
std::cout << someEvent(3, 5) << std::endl;
// Ausgabe: 15, 8, 8
Code:
namespace Utils
{
class TokenManager
{
public:
TokenManager() : m_curMaxToken(0) {}
TokenManager(TokenManager &&other) :
m_curMaxToken(other.m_curMaxToken),
m_freeTokens(std::move(other.m_freeTokens)) {
other.m_curMaxToken = 0;
}
TokenManager& operator = (TokenManager &&other) {
m_curMaxToken = other.m_curMaxToken;
m_freeTokens = std::move(other.m_freeTokens);
other.m_curMaxToken = 0;
return *this;
}
uint64 get() {
if (m_freeTokens.empty())
return ++m_curMaxToken;
auto itr = m_freeTokens.begin();
uint64 curToken = *itr;
m_freeTokens.erase(itr);
return curToken;
}
void remove(const uint64 &token) {
if (token != 0) {
if (token == m_curMaxToken)
--m_curMaxToken;
else if (token < m_curMaxToken)
m_freeTokens.insert(token);
}
}
bool is_free(const uint64 &token) const {
return token > m_curMaxToken ||
m_freeTokens.find(token) != m_freeTokens.end();
}
void reset() {
m_curMaxToken = 0;
m_freeTokens.clear();
}
protected:
uint64 m_curMaxToken;
std::set<uint64> m_freeTokens;
};
}