基于观察者模式的ISO C++信号槽实现
下面是一个符合ISO C++标准、不使用Qt的观察者模式信号槽实现:
1. 核心接口设计
cpp
#include <functional>
#include <vector>
#include <memory>
#include <algorithm>
#include <mutex>
#include <stdexcept>
// 前置声明
template<typename... Args>
class Signal;
// 连接句柄,用于管理连接的生命周期
class Connection {
private:
std::function<void()> disconnectFunc;
bool connected;
public:
Connection() : connected(false) {}
Connection(std::function<void()> func) : disconnectFunc(func), connected(true) {}
// 禁止拷贝,允许移动
Connection(const Connection&) = delete;
Connection& operator=(const Connection&) = delete;
Connection(Connection&& other) noexcept
: disconnectFunc(std::move(other.disconnectFunc)), connected(other.connected) {
other.connected = false;
}
Connection& operator=(Connection&& other) noexcept {
if (this != &other) {
disconnect();
disconnectFunc = std::move(other.disconnectFunc);
connected = other.connected;
other.connected = false;
}
return *this;
}
~Connection() {
disconnect();
}
void disconnect() {
if (connected && disconnectFunc) {
disconnectFunc();
connected = false;
}
}
bool isConnected() const { return connected; }
};
// 槽接口基类(观察者接口)
template<typename... Args>
class SlotBase {
public:
virtual ~SlotBase() = default;
virtual void call(Args... args) = 0;
virtual bool isSame(const SlotBase<Args...>* other) const = 0;
};
// 普通函数槽
template<typename... Args>
class FunctionSlot : public SlotBase<Args...> {
private:
std::function<void(Args...)> func;
public:
explicit FunctionSlot(std::function<void(Args...)> f) : func(std::move(f)) {}
void call(Args... args) override {
if (func) {
func(args...);
}
}
bool isSame(const SlotBase<Args...>* other) const override {
auto* otherFunc = dynamic_cast<const FunctionSlot<Args...>*>(other);
if (!otherFunc) return false;
// 比较函数对象(简化实现,实际可能需要更复杂的比较)
return true; // 简化处理
}
};
// 成员函数槽
template<typename T, typename... Args>
class MemberFunctionSlot : public SlotBase<Args...> {
private:
T* object;
void (T::*memberFunc)(Args...);
public:
MemberFunctionSlot(T* obj, void (T::*func)(Args...))
: object(obj), memberFunc(func) {}
void call(Args... args) override {
if (object && memberFunc) {
(object->*memberFunc)(args...);
}
}
bool isSame(const SlotBase<Args...>* other) const override {
auto* otherMember = dynamic_cast<const MemberFunctionSlot<T, Args...>*>(other);
if (!otherMember) return false;
return object == otherMember->object && memberFunc == otherMember->memberFunc;
}
};
2. 信号类实现(被观察者)
cpp
template<typename... Args>
class Signal {
private:
struct SlotEntry {
std::unique_ptr<SlotBase<Args...>> slot;
bool blocked;
SlotEntry(std::unique_ptr<SlotBase<Args...>> s, bool b = false)
: slot(std::move(s)), blocked(b) {}
};
std::vector<SlotEntry> slots;
mutable std::recursive_mutex mutex;
bool emitting;
public:
Signal() : emitting(false) {}
~Signal() = default;
// 禁止拷贝
Signal(const Signal&) = delete;
Signal& operator=(const Signal&) = delete;
// 连接普通函数
Connection connect(std::function<void(Args...)> func) {
std::lock_guard<std::recursive_mutex> lock(mutex);
auto slot = std::make_unique<FunctionSlot<Args...>>(std::move(func));
slots.emplace_back(std::move(slot), false);
return createConnection(static_cast<int>(slots.size() - 1));
}
// 连接成员函数
template<typename T>
Connection connect(T* object, void (T::*memberFunc)(Args...)) {
std::lock_guard<std::recursive_mutex> lock(mutex);
auto slot = std::make_unique<MemberFunctionSlot<T, Args...>>(object, memberFunc);
slots.emplace_back(std::move(slot), false);
return createConnection(static_cast<int>(slots.size() - 1));
}
// 连接lambda或可调用对象
template<typename Callable>
Connection connect(Callable&& callable) {
return connect(std::function<void(Args...)>(std::forward<Callable>(callable)));
}
// 发射信号(通知所有观察者)
void emit(Args... args) {
std::lock_guard<std::recursive_mutex> lock(mutex);
if (emitting) {
throw std::runtime_error("Reentrant signal emission detected");
}
emitting = true;
try {
for (auto& entry : slots) {
if (!entry.blocked && entry.slot) {
entry.slot->call(args...);
}
}
emitting = false;
} catch (...) {
emitting = false;
throw;
}
}
// 函数调用操作符重载
void operator()(Args... args) {
emit(args...);
}
// 断开所有连接
void disconnectAll() {
std::lock_guard<std::recursive_mutex> lock(mutex);
slots.clear();
}
// 阻塞指定连接
void blockConnection(const Connection& conn, bool blocked = true) {
std::lock_guard<std::recursive_mutex> lock(mutex);
// 通过连接找到对应的槽并设置阻塞状态
// 简化实现,实际需要更复杂的查找逻辑
}
// 获取连接数量
size_t connectionCount() const {
std::lock_guard<std::recursive_mutex> lock(mutex);
return slots.size();
}
private:
Connection createConnection(int index) {
return Connection([this, index]() {
std::lock_guard<std::recursive_mutex> lock(mutex);
if (index >= 0 && index < static_cast<int>(slots.size())) {
slots[index].slot.reset(); // 清空槽指针,等效于断开连接
}
});
}
};
3. 带返回值的信号槽系统
cpp
// 支持返回值的信号槽(收集所有槽的返回值)
template<typename ReturnType, typename... Args>
class SignalWithReturn {
private:
std::vector<std::function<ReturnType(Args...)>> slots;
mutable std::mutex mutex;
public:
Connection connect(std::function<ReturnType(Args...)> func) {
std::lock_guard<std::mutex> lock(mutex);
slots.push_back(func);
int index = static_cast<int>(slots.size() - 1);
return Connection([this, index]() {
std::lock_guard<std::mutex> lock(mutex);
if (index >= 0 && index < static_cast<int>(slots.size())) {
slots[index] = nullptr; // 标记为断开
}
});
}
// 发射信号并收集所有返回值
std::vector<ReturnType> emit(Args... args) {
std::vector<ReturnType> results;
std::lock_guard<std::mutex> lock(mutex);
for (auto& slot : slots) {
if (slot) {
results.push_back(slot(args...));
}
}
// 清理已断开的连接
slots.erase(
std::remove_if(slots.begin(), slots.end(),
[](const std::function<ReturnType(Args...)>& func) {
return !func;
}),
slots.end()
);
return results;
}
};
4. 使用示例
cpp
#include <iostream>
#include <string>
// 被观察者(信号发送者)
class Button {
private:
Signal<int, std::string> clicked; // 点击信号
Signal<> hovered; // 悬停信号
public:
Connection onClick(std::function<void(int, std::string)> handler) {
return clicked.connect(handler);
}
Connection onHover(std::function<void()> handler) {
return hovered.connect(handler);
}
void simulateClick(int x, const std::string& buttonName) {
std::cout << "Button clicked at (" << x << "): " << buttonName << std::endl;
clicked.emit(x, buttonName);
}
void simulateHover() {
std::cout << "Button hovered" << std::endl;
hovered.emit();
}
};
// 观察者1
class Logger {
public:
void logClick(int x, const std::string& name) {
std::cout << "[Logger] Click logged: " << name << " at x=" << x << std::endl;
}
};
// 观察者2
class UIManager {
public:
void handleClick(int x, const std::string& name) {
std::cout << "[UIManager] Processing click on " << name << std::endl;
}
void handleHover() {
std::cout << "[UIManager] Button is being hovered" << std::endl;
}
};
int main() {
Button button;
Logger logger;
UIManager uiManager;
// 连接信号和槽
Connection conn1 = button.onClick([&logger](int x, const std::string& name) {
logger.logClick(x, name);
});
Connection conn2 = button.onClick([&uiManager](int x, const std::string& name) {
uiManager.handleClick(x, name);
});
Connection conn3 = button.onHover([&uiManager]() {
uiManager.handleHover();
});
// 发射信号
button.simulateClick(100, "OK Button");
button.simulateHover();
// 断开一个连接
std::cout << "\nDisconnecting first handler...\n";
conn1.disconnect();
// 再次发射信号
button.simulateClick(200, "Cancel Button");
// Connection对象离开作用域会自动断开连接
{
Connection tempConn = button.onClick([](int x, const std::string& name) {
std::cout << "[Temporary] " << name << " clicked" << std::endl;
});
button.simulateClick(300, "Test Button");
}
// tempConn已断开
button.simulateClick(400, "Final Button");
return 0;
}
5. 线程安全的生产者-消费者示例
cpp
#include <thread>
#include <chrono>
#include <queue>
class MessageQueue {
private:
Signal<const std::string&> messageReceived;
std::queue<std::string> messages;
std::mutex queueMutex;
std::thread workerThread;
bool running;
public:
MessageQueue() : running(true) {
workerThread = std::thread(&MessageQueue::processMessages, this);
}
~MessageQueue() {
running = false;
if (workerThread.joinable()) {
workerThread.join();
}
}
Connection onMessage(std::function<void(const std::string&)> handler) {
return messageReceived.connect(handler);
}
void postMessage(const std::string& msg) {
std::lock_guard<std::mutex> lock(queueMutex);
messages.push(msg);
}
private:
void processMessages() {
while (running) {
std::string msg;
{
std::lock_guard<std::mutex> lock(queueMutex);
if (!messages.empty()) {
msg = messages.front();
messages.pop();
}
}
if (!msg.empty()) {
messageReceived.emit(msg);
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
};
// 使用示例
int main() {
MessageQueue queue;
// 订阅消息
Connection conn = queue.onMessage([](const std::string& msg) {
std::cout << "Received: " << msg << std::endl;
});
// 生产消息
for (int i = 0; i < 5; ++i) {
queue.postMessage("Message " + std::to_string(i + 1));
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return 0;
}
设计特点
- 纯ISO C++:仅使用标准库,无平台依赖
- 类型安全:模板确保类型匹配
- 线程安全:使用mutex保护共享数据
- 资源管理:Connection RAII模式自动管理连接生命周期
- 灵活性:支持普通函数、成员函数、lambda表达式
- 可扩展性:易于添加新功能(如连接优先级、一次性连接等)
此文章为AI所写,仅为保存个人学习所用。