|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
|
|
#include <functional>
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
#include <atomic>
|
|
|
|
|
|
#include <mutex>
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
#include "ScopedConnection.h"
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Signal <EFBFBD>࣬<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD>֪ͨ
|
|
|
|
|
|
* <EFBFBD>̰߳<EFBFBD>ȫ<EFBFBD><EFBFBD>֧<EFBFBD><EFBFBD> RAII <EFBFBD><EFBFBD><EFBFBD>ӹ<EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
*/
|
|
|
|
|
|
template <typename... Args>
|
|
|
|
|
|
class Signal
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
using Listener = std::function<void(Args...)>;
|
|
|
|
|
|
using Token = size_t;
|
|
|
|
|
|
|
|
|
|
|
|
Signal() : m_impl(std::make_shared<Impl>()) {}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD>
|
|
|
|
|
|
Signal(Signal&&) = default;
|
|
|
|
|
|
Signal& operator=(Signal&&) = default;
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ֹ<EFBFBD><D6B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD>ײ<EFBFBD><D7B2><EFBFBD> shared_ptr<74><72><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ᵼ<EFBFBD><E1B5BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Signal <20><><EFBFBD><EFBFBD>ͬһ<CDAC><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><D0B1><EFBFBD>ͨ<EFBFBD><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ԥ<EFBFBD>ڣ<EFBFBD>
|
|
|
|
|
|
Signal(const Signal&) = delete;
|
|
|
|
|
|
Signal& operator=(const Signal&) = delete;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief <EFBFBD><EFBFBD><EFBFBD>Ӽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
*/
|
|
|
|
|
|
Token Connect(Listener listener)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard lock(m_impl->mutex);
|
|
|
|
|
|
Token token = ++m_impl->tokenCounter;
|
|
|
|
|
|
m_impl->listeners.emplace(token, std::move(listener));
|
|
|
|
|
|
return token;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief <EFBFBD>Ƴ<EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool Disconnect(Token token)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard lock(m_impl->mutex);
|
|
|
|
|
|
return m_impl->listeners.erase(token) > 0; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƴд<C6B4>ͷ<EFBFBD><CDB7><EFBFBD>ֵ
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief RAII <EFBFBD>汾<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӻ<EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
*/
|
|
|
|
|
|
[[nodiscard]]
|
|
|
|
|
|
ScopedConnection ConnectScoped(Listener listener)
|
|
|
|
|
|
{
|
|
|
|
|
|
Token token = Connect(std::move(listener));
|
|
|
|
|
|
|
|
|
|
|
|
// ʹ<><CAB9> weak_ptr <20><><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD><EFBFBD><EFBFBD>ã<EFBFBD><C3A3><EFBFBD><EFBFBD><EFBFBD>ֹҰָ<D2B0><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
std::weak_ptr<Impl> weakImpl = m_impl;
|
|
|
|
|
|
|
|
|
|
|
|
return ScopedConnection([weakImpl, token]()
|
|
|
|
|
|
{
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> weak_ptr<74><72><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Signal <20>Ѿ<EFBFBD><D1BE><EFBFBD><EFBFBD><EFBFBD><EFBFBD>٣<EFBFBD>lock <20>᷵<EFBFBD>ؿ<EFBFBD>ָ<EFBFBD><D6B8>
|
|
|
|
|
|
if (auto impl = weakImpl.lock())
|
|
|
|
|
|
{
|
|
|
|
|
|
impl->Remove(token);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief ֪ͨ<EFBFBD><EFBFBD><EFBFBD>м<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
*/
|
|
|
|
|
|
void Emit(Args... args)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::vector<Listener> listenersSnapshot;
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard lock(m_impl->mutex);
|
|
|
|
|
|
if (m_impl->listeners.empty())
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>գ<EFBFBD><D5A3><EFBFBD><EFBFBD>ص<EFBFBD><D8B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD><D6B4>
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD><D8B5>е<EFBFBD><D0B5><EFBFBD> Connect/Disconnect<63><74><EFBFBD>͵<EFBFBD><CDB5><EFBFBD><EFBFBD><EFBFBD>ʧЧ
|
|
|
|
|
|
listenersSnapshot.reserve(m_impl->listeners.size());
|
|
|
|
|
|
for (const auto& [token, listener] : m_impl->listeners)
|
|
|
|
|
|
{
|
|
|
|
|
|
listenersSnapshot.push_back(listener);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& listener : listenersSnapshot)
|
|
|
|
|
|
{
|
|
|
|
|
|
listener(args...);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>м<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
*/
|
|
|
|
|
|
void Clear()
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard lock(m_impl->mutex);
|
|
|
|
|
|
m_impl->listeners.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
// <20>ڲ<EFBFBD>ʵ<EFBFBD><CAB5><EFBFBD>࣬<EFBFBD><E0A3AC> shared_ptr <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
struct Impl
|
|
|
|
|
|
{
|
|
|
|
|
|
std::mutex mutex;
|
|
|
|
|
|
std::unordered_map<Token, Listener> listeners;
|
|
|
|
|
|
std::atomic_size_t tokenCounter{ 0 };
|
|
|
|
|
|
|
|
|
|
|
|
void Remove(Token token)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard lock(mutex);
|
|
|
|
|
|
listeners.erase(token);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<Impl> m_impl;
|
|
|
|
|
|
};
|