基于观察者模式的ISO C++信号槽实现

基于观察者模式的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;
}

设计特点

  1. 纯ISO C++:仅使用标准库,无平台依赖
  2. 类型安全:模板确保类型匹配
  3. 线程安全:使用mutex保护共享数据
  4. 资源管理:Connection RAII模式自动管理连接生命周期
  5. 灵活性:支持普通函数、成员函数、lambda表达式
  6. 可扩展性:易于添加新功能(如连接优先级、一次性连接等)

此文章为AI所写,仅为保存个人学习所用。

相关推荐
Wang's Blog3 小时前
Nodejs-HardCore: Buffer操作、Base64编码与zlib压缩实战
开发语言·nodejs
csbysj20204 小时前
C# 集合(Collection)
开发语言
csbysj20204 小时前
Lua 面向对象编程
开发语言
ha20428941944 小时前
Linux操作系统学习记录之---TcpSocket
linux·网络·c++·学习
Java追光着5 小时前
gemini pro 此账号无法订阅Google AI方案
ai·gemini
AI视觉网奇5 小时前
ue5 插件 WebSocket
c++·ue5
左直拳5 小时前
将c++程序部署到docker
开发语言·c++·docker
少林码僧5 小时前
2.9 字段分箱技术详解:连续变量离散化,提升模型效果的关键步骤
人工智能·ai·数据分析·大模型