diff --git a/QtComponets/AsyncEvent.h b/QtComponets/AsyncEvent.h new file mode 100644 index 0000000..e437094 --- /dev/null +++ b/QtComponets/AsyncEvent.h @@ -0,0 +1,137 @@ +#ifndef ASYNCEVENT_H +#define ASYNCEVENT_H + +#include "BoostLog.h" +#include +#include +#include +#include +#include +#include +#include + +namespace Amass { + +template +class AsyncEvent : public QEvent { +public: + using Arguments = std::tuple...>; + AsyncEvent(Functor &&functor, Args &&...args) + : QEvent(static_cast(QEvent::registerEventType())), m_functor(std::forward(functor)), + m_args(std::forward(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 +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 && target->thread() == QThread::currentThread()) { + std::invoke(std::forward(functor), std::forward(args)...); + return; + } + auto event = new AsyncEvent(std::forward(functor), std::forward(args)...); + QCoreApplication::postEvent(target, event, + std::is_same_v ? Qt::LowEventPriority : Qt::NormalEventPriority); +} + +template +class MemberFunctionArgumentsTraits; + +template +class MemberFunctionArgumentsTraits { +public: + using FunctionPointerType = Ret (Object::*)(Args...); + + static auto wrapper(std::shared_ptr 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)...); + }, + std::forward(args)...); + }; + } +}; + +template +struct LambdaTraits : LambdaTraits {}; + +template +struct LambdaTraits { + template + static auto wrapper(std::shared_ptr instance, Object &&function) { + return [instanceWeakPointer{std::weak_ptr{instance}}, function = std::move(function)](Args &&...args) { + if (instanceWeakPointer.expired()) return; + auto instance = instanceWeakPointer.lock(); + executeAtObjectThread( + instance.get(), + [instanceWeakPointer, function = std::move(function)](Args &&...args) { + if (instanceWeakPointer.expired()) return; + auto instance = instanceWeakPointer.lock(); + function(std::forward(args)...); + }, + std::forward(args)...); + }; + } +}; + +template +struct LambdaTraits { + + template + static auto wrapper(std::shared_ptr instance, Object &&function) { + return [instanceWeakPointer{std::weak_ptr{instance}}, function = std::move(function)](Args &&...args) { + if (instanceWeakPointer.expired()) return; + auto instance = instanceWeakPointer.lock(); + executeAtObjectThread( + instance.get(), + [instanceWeakPointer, function = std::move(function)](Args &&...args) mutable { + if (instanceWeakPointer.expired()) return; + auto instance = instanceWeakPointer.lock(); + function(std::forward(args)...); + }, + std::forward(args)...); + }; + } +}; + +template +boost::signals2::connection connect(SignalType &signal, std::shared_ptr target, Function Object::*function) { + using Traits = Amass::MemberFunctionArgumentsTraits; + + return signal.connect(typename SignalType::slot_type(Traits::wrapper(target, function)).track_foreign(target)); +} + +template +boost::signals2::connection connect(SignalType &signal, std::shared_ptr target, Function &&function) { + using Traits = LambdaTraits; + return signal.connect( + typename SignalType::slot_type(Traits::template wrapper(target, std::move(function))).track_foreign(target)); +} + +} // namespace Amass +#endif // ASYNCEVENT_H diff --git a/QtComponets/CMakeLists.txt b/QtComponets/CMakeLists.txt index adda7ca..478091c 100644 --- a/QtComponets/CMakeLists.txt +++ b/QtComponets/CMakeLists.txt @@ -3,6 +3,7 @@ find_package(Qt6 COMPONENTS Gui REQUIRED) set(CMAKE_AUTOMOC ON) add_library(QtComponets + AsyncEvent.h QClassStdStream.h QClassStdStream.cpp )