You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
3.0 KiB
C

1 month ago
#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;
};