/** * @RDE_HEADER@ * General Qt-connection * $Id$ * $HeadURL$ * $Author$ * Created by Christian Fröbel, SLSE, 2017-07-24 */ #pragma once #include #include #include #include #include namespace LtsUtils { /** * IConnection represents a general Qt-connection. * It is useful in cases where you need to store different Qt-connections * for later use. * @author Christian Fröbel */ class IConnection { public: virtual ~IConnection() = default; virtual void connect(Qt::ConnectionType = Qt::AutoConnection) = 0; virtual void disconnect() = 0; }; /** * A DummyConnection can be used when there is no connection. * connect() and disconnect() are no-ops. */ class DummyConnection : public IConnection { public: void connect(Qt::ConnectionType = Qt::AutoConnection) override {} void disconnect() override {} }; namespace detail { /** * Helper class to extract the class type from a given pointer to member, * where member can be an object or a function (aka method). * We need it only for methods though. * This is just the main template declaration without implementation. */ template struct ExtractClass; /** * Specialization vor pointer to member. */ template struct ExtractClass { using type = T; }; } // namespace detail /** * Implements a Qt-connection based on the pointer to the methods being connected. * The connection can be signal to signal or signal to slot. * * Connection is guarded against the case where either the signal-emitter * or -target is deleted before conntect() is actually called. * * @param Signal Pointer to method of the Emitter * @param Slot Pointer to method of the Target; might also be a signal. * @author Christian Fröbel */ template class Connection : public IConnection { static_assert(std::is_member_function_pointer::value, "Must be a pointer to method"); static_assert(std::is_member_function_pointer::value, "Must be a pointer to method"); using Emitter = typename detail::ExtractClass::type; using Target = typename detail::ExtractClass::type; static_assert(std::is_base_of::value, "Must be derived from QObject"); static_assert(std::is_base_of::value, "Must be derived from QObject"); public: Connection(Emitter *emitter, Signal signal, Target *target, Slot slot): m_emitter(emitter), m_signal(signal), m_target(target), m_slot(slot) { Q_ASSERT(emitter); Q_ASSERT(signal); Q_ASSERT(target); Q_ASSERT(slot); } void connect(Qt::ConnectionType connectionType = Qt::AutoConnection) override { Q_ASSERT(!m_connected); m_connected = true; // Guard against the possibility when either m_emitter or m_target got deleted before // the Connection is actually connected. if (!m_emitter || !m_target) return; m_conn = QObject::connect(m_emitter.data(), m_signal, m_target.data(), m_slot, connectionType); Q_ASSERT(m_conn); } void disconnect() override { Q_ASSERT(m_connected); if (m_conn) QObject::disconnect(m_conn); m_connected = false; } private: bool m_connected{false}; QPointer m_emitter; Signal m_signal; QPointer m_target; Slot m_slot; QMetaObject::Connection m_conn; }; /** * Helper function in order to deduce the template parameters. */ template Connection* createConnection(typename detail::ExtractClass::type *emitter, Signal signal, typename detail::ExtractClass::type *target, Slot slot) { return new Connection(emitter, signal, target, slot); } } // namespace LtsUtils