Kylin/Fluent/AsyncEvent.h
2024-08-20 23:58:02 +08:00

138 lines
5.3 KiB
C++

#ifndef ASYNCEVENT_H
#define ASYNCEVENT_H
#include "BoostLog.h"
#include <QCoreApplication>
#include <QEvent>
#include <QObject>
#include <QThread>
#include <boost/signals2/signal.hpp>
#include <memory>
#include <tuple>
namespace Amass {
template <typename Functor, typename... Args>
class AsyncEvent : public QEvent {
public:
using Arguments = std::tuple<std::decay_t<Args>...>;
AsyncEvent(Functor &&functor, Args &&...args)
: QEvent(static_cast<QEvent::Type>(QEvent::registerEventType())), m_functor(std::forward<Functor>(functor)),
m_args(std::forward<Args>(args)...) {
}
~AsyncEvent() {
// LOG(debug) << "AsyncEvent::~AsyncEvent: " << this;
if (QCoreApplication::closingDown()) {
LOG(warning) << "QCoreApplication closed,skip handle task.";
return;
}
std::apply(m_functor, std::move(m_args));
}
private:
Functor m_functor;
Arguments m_args;
};
using HighPriority = std::true_type;
using LowPriority = std::false_type;
template <typename Priority = LowPriority, typename Functor, typename... Args>
inline void executeAtObjectThread(QObject *target, Functor &&functor, Args &&...args) {
if (QCoreApplication::closingDown()) {
LOG(warning) << "QCoreApplication closed,skip handle task.";
return;
}
if (std::is_same_v<Priority, HighPriority> && target->thread() == QThread::currentThread()) {
std::invoke(std::forward<Functor>(functor), std::forward<Args>(args)...);
return;
}
auto event = new AsyncEvent<Functor, Args...>(std::forward<Functor>(functor), std::forward<Args>(args)...);
QCoreApplication::postEvent(target, event,
std::is_same_v<Priority, LowPriority> ? Qt::LowEventPriority : Qt::NormalEventPriority);
}
template <typename Function>
class MemberFunctionArgumentsTraits;
template <typename Object, typename Ret, typename... Args>
class MemberFunctionArgumentsTraits<Ret (Object::*)(Args...)> {
public:
using FunctionPointerType = Ret (Object::*)(Args...);
static auto wrapper(std::shared_ptr<Object> instance, FunctionPointerType function) {
return [instanceWeakPointer{std::weak_ptr{instance}}, function](Args &&...args) {
if (instanceWeakPointer.expired()) return;
auto instance = instanceWeakPointer.lock();
executeAtObjectThread(
instance.get(),
[instanceWeakPointer, function](Args &&...args) {
if (instanceWeakPointer.expired()) return;
auto instance = instanceWeakPointer.lock();
(instance.get()->*function)(std::forward<Args>(args)...);
},
std::forward<Args>(args)...);
};
}
};
template <class Ld>
struct LambdaTraits : LambdaTraits<decltype(&Ld::operator())> {};
template <class Ret, class Object, class... Args>
struct LambdaTraits<Ret (Object::*)(Args...) const> {
template <typename T = std::false_type>
static auto wrapper(std::shared_ptr<QObject> instance, Object &&function) {
return [instanceWeakPointer{std::weak_ptr<QObject>{instance}}, function = std::move(function)](Args &&...args) {
if (instanceWeakPointer.expired()) return;
auto instance = instanceWeakPointer.lock();
executeAtObjectThread<T>(
instance.get(),
[instanceWeakPointer, function = std::move(function)](Args &&...args) {
if (instanceWeakPointer.expired()) return;
auto instance = instanceWeakPointer.lock();
function(std::forward<Args>(args)...);
},
std::forward<Args>(args)...);
};
}
};
template <class Ret, class Object, class... Args>
struct LambdaTraits<Ret (Object::*)(Args...)> {
template <typename T = std::false_type>
static auto wrapper(std::shared_ptr<QObject> instance, Object &&function) {
return [instanceWeakPointer{std::weak_ptr<QObject>{instance}}, function = std::move(function)](Args &&...args) {
if (instanceWeakPointer.expired()) return;
auto instance = instanceWeakPointer.lock();
executeAtObjectThread<T>(
instance.get(),
[instanceWeakPointer, function = std::move(function)](Args &&...args) mutable {
if (instanceWeakPointer.expired()) return;
auto instance = instanceWeakPointer.lock();
function(std::forward<Args>(args)...);
},
std::forward<Args>(args)...);
};
}
};
template <typename SignalType, class Function, class Object>
boost::signals2::connection connect(SignalType &signal, std::shared_ptr<Object> target, Function Object::*function) {
using Traits = Amass::MemberFunctionArgumentsTraits<decltype(function)>;
return signal.connect(typename SignalType::slot_type(Traits::wrapper(target, function)).track_foreign(target));
}
template <typename T = std::false_type, typename SignalType, class Function>
boost::signals2::connection connect(SignalType &signal, std::shared_ptr<QObject> target, Function &&function) {
using Traits = LambdaTraits<Function>;
return signal.connect(
typename SignalType::slot_type(Traits::template wrapper<T>(target, std::move(function))).track_foreign(target));
}
} // namespace Amass
#endif // ASYNCEVENT_H