138 lines
5.3 KiB
C++
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
|