palacaze/sigslot

can connect fun run in specified thread? like qt queue connection, thanks and merry merry christmas !!! (-:

maketardis opened this issue · 2 comments

can connect fun run in specified thread? like qt queue connection, thanks and merry merry christmas !!! (-:

Sigslot does not provide any way of executing slots in the receiver thread.

In order to work, much like Qt, every thread would need an event loop to schedule the execution of the slot in the correct thread.

However, if you are working with the Qt framework, and assuming you want to execute your slots in the main threads, here is a piece of code I have been using in my own code to execute slots in the main thread.

#include <QCoreApplication>
#include <QThread>
#include <tuple>
#include <type_traits>

/// Execute a callable in the QApplication thread
template <typename Func>
void runInMainThread(Func && f) {
    static QThread *main_thread = QCoreApplication::instance()->thread();
    if (QThread::currentThread() == main_thread)
        f();
    else
        QMetaObject::invokeMethod(QCoreApplication::instance(), f);
}

namespace detail {

template<typename F, typename T, size_t... Is>
inline constexpr decltype(auto) applyImpl(std::index_sequence<Is...>, F && f, T && t) {
    return std::forward<F>(f)(std::get<Is>(std::forward<T>(t))..., std::forward<A>(a)...);
}

/// c++17 std::apply
template<typename F, typename T>
inline constexpr decltype(auto) apply(F && f, T && t) {
    return applyImpl(
        std::make_index_sequence<std::tuple_size<std::decay_t<T>>::value>{},
        std::forward<F>(f),
        std::forward<T>(t));
}

template <typename Func, typename Obj, typename... T>
sigslot::connection connectGui(sigslot::signal<T...> &sig, Func && func, Obj && tracker) {
    return sig.connect([f=std::forward<Func>(func)](auto && ...t) {
        runInMainThread([=, args=std::tuple<std::decay_t<decltype(t)>...>{std::forward<decltype(t)>(t)...}] {
            detail::apply(f, args);
        });
    }, std::forward<Obj>(tracker));
}

} // namespace detail

/**
 * Connection of a pointer to member function of a QObject to a signal
 *
 * The Callable is guaranteed to be called in the thread where the QObject lives.
 * If the QObject gets destroyed, the signal will be automatically disconnected.
 *
 * @param sig a signal
 * @param obj a QObject
 * @param pmf pointer to member function of the QObject
 * @return a connection object
 */
template <typename Obj, typename Pmf, typename... T, std::enable_if_t<std::is_base_of<QObject, Obj>::value>* = nullptr>
connection connectGui(sigslot::signal<T...> &sig, Obj *obj, Pmf && pmf) {
    return detail::connectGui(sig,
        [obj, p=std::forward<Pmf>(pmf)](auto && ...t) {
            ((*obj).*p)(std::forward<decltype(t)>(t)...);
        }, obj);
}

/**
 * Connection of a function making calls to Qt Gui / Widgets to a signal
 *
 * The Callable is guaranteed to be called in the QApplication thread.
 *
 * @param sig a signal
 * @param func a function slot
 * @return a connection object
 */
template <typename Func, typename... T>
connection connectGui(sigslot::signal<T...> &sig, Func && func) {
    return detail::connectGui(sig, std::forward<Func>(func), QCoreApplication::instance());
}

thanks so much! It helps a lots! I will try it! Happy new year by the way!!! (-: