|
|
#pragma once
|
|
|
|
|
|
#include <functional>
|
|
|
#include <vector>
|
|
|
#include <unordered_map>
|
|
|
#include <atomic>
|
|
|
#include <mutex>
|
|
|
#include <memory>
|
|
|
#include "ScopedConnection.h"
|
|
|
|
|
|
/**
|
|
|
* Signal 类,用于事件通知
|
|
|
* 线程安全,支持 RAII 连接管理
|
|
|
*/
|
|
|
template <typename... Args>
|
|
|
class Signal
|
|
|
{
|
|
|
public:
|
|
|
using Listener = std::function<void(Args...)>;
|
|
|
using Token = size_t;
|
|
|
|
|
|
Signal() : m_impl(std::make_shared<Impl>()) {}
|
|
|
|
|
|
// 允许移动
|
|
|
Signal(Signal&&) = default;
|
|
|
Signal& operator=(Signal&&) = default;
|
|
|
|
|
|
// 禁止拷贝(因为底层是 shared_ptr,拷贝会导致两个 Signal 共享同一个监听器列表,通常不符合预期)
|
|
|
Signal(const Signal&) = delete;
|
|
|
Signal& operator=(const Signal&) = delete;
|
|
|
|
|
|
/**
|
|
|
* @brief 添加监听器
|
|
|
*/
|
|
|
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 移除指定令牌的监听器
|
|
|
*/
|
|
|
bool Disconnect(Token token)
|
|
|
{
|
|
|
std::lock_guard lock(m_impl->mutex);
|
|
|
return m_impl->listeners.erase(token) > 0; // 修正了拼写和返回值
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief RAII 版本的连接函数
|
|
|
*/
|
|
|
[[nodiscard]]
|
|
|
ScopedConnection ConnectScoped(Listener listener)
|
|
|
{
|
|
|
Token token = Connect(std::move(listener));
|
|
|
|
|
|
// 使用 weak_ptr 打破循环引用,并防止野指针访问
|
|
|
std::weak_ptr<Impl> weakImpl = m_impl;
|
|
|
|
|
|
return ScopedConnection([weakImpl, token]()
|
|
|
{
|
|
|
// 尝试锁定 weak_ptr,如果 Signal 已经被销毁,lock 会返回空指针
|
|
|
if (auto impl = weakImpl.lock())
|
|
|
{
|
|
|
impl->Remove(token);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief 通知所有监听器
|
|
|
*/
|
|
|
void Emit(Args... args)
|
|
|
{
|
|
|
std::vector<Listener> listenersSnapshot;
|
|
|
|
|
|
{
|
|
|
std::lock_guard lock(m_impl->mutex);
|
|
|
if (m_impl->listeners.empty())
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 创建快照:将回调拷贝出来,在锁外执行
|
|
|
// 避免死锁(回调中调用 Connect/Disconnect)和迭代器失效
|
|
|
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 清空所有监听器
|
|
|
*/
|
|
|
void Clear()
|
|
|
{
|
|
|
std::lock_guard lock(m_impl->mutex);
|
|
|
m_impl->listeners.clear();
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
// 内部实现类,由 shared_ptr 管理生命周期
|
|
|
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;
|
|
|
}; |