支持 int64 和string两种类型,同步和异步,连接和取消连接
发送信号尽量使用
sig.sync<int>(0, 1);这种方式
sig.sync(0, 1); 可能会有问题?
clear disconnect 同理
cpp
#pragma once
#include <functional>
#include <mutex>
#include <vector>
#include <system_error>
#include <unordered_map>
#include <string>
#include <memory>
#include <typeinfo>
#include <cstdint>
#include <tuple>
#include <iostream>
#include <thread>
class Signal {
public:
template <typename... Args>
std::error_code connect(const std::string& key,
std::function<void(std::decay_t<Args>...)> slot) {
return connectImpl<std::string, Args...>(key, std::move(slot));
}
template <typename... Args, typename Obj, typename MemFn>
std::error_code connect(const std::string& key, Obj* obj, MemFn mf) {
return connectMember<std::string, Args...>(key, obj, mf);
}
template <typename... Args>
std::error_code connect(int64_t key,
std::function<void(std::decay_t<Args>...)> slot) {
return connectImpl<int64_t, Args...>(key, std::move(slot));
}
template <typename... Args, typename Obj, typename MemFn>
std::error_code connect(int64_t key, Obj* obj, MemFn mf) {
return connectMember<int64_t, Args...>(key, obj, mf);
}
template <typename... Args, typename Obj, typename MemFn>
std::size_t disconnect(const std::string& key, Obj* obj, MemFn mf) {
return disconnectImpl<std::string, Args...>(key, obj, mf);
}
template <typename... Args, typename Obj, typename MemFn>
std::size_t disconnect(int64_t key, Obj* obj, MemFn mf) {
return disconnectImpl<int64_t, Args...>(key, obj, mf);
}
template <typename... Args>
void clear(const std::string& key) { clearImpl<std::string, Args...>(key); }
template <typename... Args>
void clear(int64_t key) { clearImpl<int64_t, Args...>(key); }
template <typename... Args>
int sync(const std::string& key, Args&&... args) const {
return syncImpl<std::string, Args...>(key, std::forward<Args>(args)...);
}
template <typename... Args>
int sync(int64_t key, Args&&... args) const {
return syncImpl<int64_t, Args...>(key, std::forward<Args>(args)...);
}
template <typename... Args>
void async(const std::string& key, Args&&... args) const {
auto* ctx = context<std::string, std::decay_t<Args>...>(key);
std::thread([ctx, tup = std::make_tuple(std::forward<Args>(args)...)]() mutable {
// std::cout << "[async thread " << std::this_thread::get_id() << "]\n";
std::apply([ctx](auto&&... as) { ctx->fire(std::forward<decltype(as)>(as)..., true); }, tup);
}).detach();
}
template <typename... Args>
void async(int64_t key, Args&&... args) const {
auto* ctx = context<int64_t, std::decay_t<Args>...>(key);
std::thread([ctx, tup = std::make_tuple(std::forward<Args>(args)...)]() mutable {
// std::cout << "[async thread " << std::this_thread::get_id() << "]\n";
std::apply([ctx](auto&&... as) { ctx->fire(std::forward<decltype(as)>(as)..., true); }, tup);
}).detach();
}
private:
template <typename T>
static const void* bitCast(T mf) { union { T in; const void* out; } u{mf}; return u.out; }
template <typename Key, typename... Args>
struct Context {
using Slot = std::function<void(Args...)>;
struct Node {
Slot func;
void* obj = nullptr;
const void* mf = nullptr;
size_t mfHash = 0;
std::shared_ptr<void> holder;
};
mutable std::mutex m_;
std::vector<std::unique_ptr<Node>> nodes_;
void append(Slot f, void* objPtr, const void* mfPtr, size_t hash, std::shared_ptr<void> keep = {}) {
std::lock_guard lg(m_);
auto n = std::make_unique<Node>();
n->func = std::move(f);
n->obj = objPtr;
n->mf = mfPtr;
n->mfHash = hash;
n->holder = std::move(keep);
nodes_.push_back(std::move(n));
}
void keepAlive(const std::shared_ptr<Slot>& sp) {
std::lock_guard lg(m_);
if (!nodes_.empty()) nodes_.back()->holder = sp;
}
std::size_t remove(void* obj, const void* mf, size_t hash) {
std::lock_guard lg(m_);
auto old = nodes_.size();
nodes_.erase(std::remove_if(nodes_.begin(), nodes_.end(),
[=](const auto& n) {
return n->mfHash == hash && n->obj == obj && n->mf == mf;
}),
nodes_.end());
return old - nodes_.size();
}
void clear() {
std::lock_guard lg(m_);
nodes_.clear();
}
int fire(Args... args, bool) const {
std::vector<Node*> snap;
{
std::lock_guard lg(m_);
snap.reserve(nodes_.size());
for (auto& n : nodes_) snap.push_back(n.get());
}
for (auto* p : snap) p->func(args...);
return 0;
}
};
template <typename Key, typename... Args>
static Context<Key, Args...>* context(const Key& key) {
static std::mutex g_m;
static std::unordered_map<Key, std::unique_ptr<Context<Key, Args...>>> pool;
std::lock_guard lg(g_m);
auto& ptr = pool[key];
if (!ptr) ptr = std::make_unique<Context<Key, Args...>>();
return ptr.get();
}
template <typename Key, typename... Args>
std::error_code connectImpl(const Key& key, std::function<void(std::decay_t<Args>...)> slot) {
context<Key, Args...>(key)->append(std::move(slot), nullptr, nullptr, 0);
return {};
}
template <typename Key, typename... Args, typename Obj, typename MemFn>
std::error_code connectMember(const Key& key, Obj* obj, MemFn mf) {
auto sp = std::make_shared<std::function<void(std::decay_t<Args>...)>>(
[obj, mf](std::decay_t<Args>... args) { (obj->*mf)(std::forward<std::decay_t<Args>>(args)...); });
auto* ctx = context<Key, Args...>(key);
ctx->append(*sp, obj, bitCast(mf), typeid(MemFn).hash_code(), sp);
return {};
}
template <typename Key, typename... Args, typename Obj, typename MemFn>
std::size_t disconnectImpl(const Key& key, Obj* obj, MemFn mf) {
return context<Key, Args...>(key)->remove(obj, bitCast(mf), typeid(MemFn).hash_code());
}
template <typename Key, typename... Args>
void clearImpl(const Key& key) { context<Key, Args...>(key)->clear(); }
template <typename Key, typename... Args>
int syncImpl(const Key& key, Args&&... args) const {
return context<Key, Args...>(key)->fire(std::forward<Args>(args)..., false);
}
};
/*>>>>>>>> 测试 <<<<<<<<*/
#include <iostream>
class Foo {
public:
void bar(int x) { std::cout << x << " " << std::this_thread::get_id()<<std::endl;}
};
int main() {
std::cout << "main " << std::this_thread::get_id()<<std::endl;
Signal sig;
Foo f;
sig.connect<int>("s", &f, &Foo::bar);
sig.connect<int>(0, &f, &Foo::bar);
sig.sync<int>(0, 1);
sig.sync<int>("s", 2);
sig.async<int>(0, 3);
sig.async<int>("s", 4);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::cout <<std::endl;
sig.clear<int>("s");
sig.sync<int>(0, 1);
sig.sync<int>("s", 2);
sig.async<int>(0, 3);
sig.async<int>("s", 4);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::cout <<std::endl;
// sig.disconnect<int>("s", &f, &Foo::bar);
sig.disconnect<int>(0, &f, &Foo::bar);
sig.sync<int>(0, 1);
sig.sync<int>("s", 2);
sig.async<int>(0, 3);
sig.async<int>("s", 4);
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
std::cout <<std::endl;
}