intial commit
This commit is contained in:
126
source/mijin/async/signal.hpp
Normal file
126
source/mijin/async/signal.hpp
Normal file
@@ -0,0 +1,126 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_ASYNC_SIGNAL_HPP_INCLUDED)
|
||||
#define MIJIN_ASYNC_SIGNAL_HPP_INCLUDED 1
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include "../util/flag.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// public defines
|
||||
//
|
||||
|
||||
//
|
||||
// public constants
|
||||
//
|
||||
|
||||
//
|
||||
// public types
|
||||
//
|
||||
|
||||
MIJIN_DEFINE_FLAG(Oneshot);
|
||||
|
||||
template<typename... TArgs>
|
||||
class Signal
|
||||
{
|
||||
public:
|
||||
using handler_t = std::function<void(TArgs...)>;
|
||||
using token_t = std::uint32_t;
|
||||
private:
|
||||
struct RegisteredHandler
|
||||
{
|
||||
handler_t callable;
|
||||
std::weak_ptr<void> referenced;
|
||||
token_t token;
|
||||
Oneshot oneshot = Oneshot::NO;
|
||||
};
|
||||
using handler_vector_t = std::vector<RegisteredHandler>;
|
||||
private:
|
||||
handler_vector_t handlers_;
|
||||
token_t nextToken = 1;
|
||||
std::mutex handlersMutex_;
|
||||
public:
|
||||
Signal() = default;
|
||||
Signal(const Signal&) = delete;
|
||||
Signal(Signal&&) noexcept = default;
|
||||
public:
|
||||
Signal& operator=(const Signal&) = delete;
|
||||
Signal& operator=(Signal&&) noexcept = default;
|
||||
public:
|
||||
template<typename THandler, typename TWeak = void>
|
||||
inline token_t connect(THandler handler, Oneshot oneshot = Oneshot::NO, std::weak_ptr<TWeak> referenced = std::weak_ptr<TWeak>()) noexcept;
|
||||
inline void disconnect(token_t token) noexcept;
|
||||
inline void emit(TArgs&&... args) noexcept;
|
||||
};
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
template<typename... TArgs>
|
||||
template<typename THandler, typename TWeak>
|
||||
inline auto Signal<TArgs...>::connect(THandler handler, Oneshot oneshot, std::weak_ptr<TWeak> referenced) noexcept -> token_t
|
||||
{
|
||||
std::lock_guard lock(handlersMutex_);
|
||||
|
||||
auto callable = handler_t(handler);
|
||||
handlers_.push_back({
|
||||
.callable = std::move(callable),
|
||||
.referenced = std::move(referenced),
|
||||
.token = nextToken,
|
||||
.oneshot = oneshot
|
||||
});
|
||||
|
||||
return nextToken++;
|
||||
}
|
||||
|
||||
template<typename... TArgs>
|
||||
inline void Signal<TArgs...>::disconnect(token_t token) noexcept
|
||||
{
|
||||
std::lock_guard lock(handlersMutex_);
|
||||
|
||||
auto it = std::remove_if(handlers_.begin(), handlers_.end(), [token](const RegisteredHandler& handler)
|
||||
{
|
||||
return handler.token == token;
|
||||
});
|
||||
handlers_.erase(it, handlers_.end());
|
||||
}
|
||||
|
||||
template<typename... TArgs>
|
||||
inline void Signal<TArgs...>::emit(TArgs&&... args) noexcept
|
||||
{
|
||||
std::lock_guard lock(handlersMutex_);
|
||||
|
||||
// first erase any handlers with expired references
|
||||
// auto it = std::remove_if(handlers_.begin(), handlers_.end(), [](const RegisteredHandler& handler)
|
||||
// {
|
||||
// return handler.referenced.expired();
|
||||
// });
|
||||
// handlers_.erase(it, handlers_.end());
|
||||
// TODO: this doesn't really work since expired() also returns true if the pointer was never set
|
||||
|
||||
// invoke all handlers
|
||||
for (RegisteredHandler& handler : handlers_)
|
||||
{
|
||||
handler.callable(forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
// remove any oneshot
|
||||
auto it = std::remove_if(handlers_.begin(), handlers_.end(), [](const RegisteredHandler& handler)
|
||||
{
|
||||
return handler.oneshot;
|
||||
});
|
||||
handlers_.erase(it, handlers_.end());
|
||||
}
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_ASYNC_SIGNAL_HPP_INCLUDED)
|
||||
Reference in New Issue
Block a user