QObject: introduce QT_NO_CONTEXTLESS_CONNECT

The 3-arguments overload of connect() is error-prone / easy to misuse:

* it's easy to e.g. connect to a lambda, and capture local state in the
  lambda. By not passing a receiver/context, the connection won't get
  disconnected if the local state gets destroyed (effectively, it
  creates a "dangling connection");

* in a multithread scenario, one may not realize that the connection is
  forced to be direct. If the signal is emitted in another thread than
  the one establishing the connection¹, then the functor will be invoked
  in that other thread. (Not that "the thread establishing the
  connection" has ever mattered, but again, this API is error-prone.)

Add a macro that allows users to disable the overload in their project.
I'm not going for deprecation because there's simply too much code
around that uses it.

[ChangeLog][QtCore][QObject] Added the QT_NO_CONTEXTLESS_CONNECT macro.
Defining the macro before including any Qt header will disable
the overload of QObject::connect that does not take a receiver/context
argument.

Change-Id: I86af1029c1a211ea365f417aae9038d3fcacadfd
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Giuseppe D'Angelo 2023-06-24 12:39:43 +02:00
parent 7297cd808b
commit 842cfcec80
2 changed files with 29 additions and 0 deletions

View File

@ -4854,6 +4854,28 @@ QDebug operator<<(QDebug dbg, const QObject *o)
\sa QObject::connect
*/
/*!
\macro QT_NO_CONTEXTLESS_CONNECT
\relates QObject
\since 6.7
Defining this macro will the overload of QObject::connect() that
connects a signal to a functor, without also specifying a QObject
as a receiver/context object (that is, the 3-arguments overload
of QObject::connect()).
Using the context-less overload is error prone, because it is easy
to connect to functors that depend on some local state of the
receiving end. If such local state gets destroyed, the connection
does not get automatically disconnected.
Moreover, such connections are always direct connections, which may
cause issues in multithreaded scenarios (for instance, if the
signal is emitted from another thread).
\sa QObject::connect, Qt::ConnectionType
*/
/*!
\typedef QObjectList
\relates QObject
@ -4959,6 +4981,11 @@ void qDeleteInEventHandler(QObject *o)
However, you should take care that any objects used within the functor
are still alive when the signal is emitted.
For this reason, it is recommended to use the overload of connect()
that also takes a QObject as a receiver/context. It is possible
to disable the usage of the context-less overload by defining the
\c{QT_NO_CONTEXTLESS_CONNECT} macro.
Overloaded functions can be resolved with help of \l qOverload.
*/

View File

@ -235,6 +235,7 @@ public:
type, types, &SignalType::Object::staticMetaObject);
}
#ifndef QT_NO_CONTEXTLESS_CONNECT
//connect without context
template <typename Func1, typename Func2>
static inline QMetaObject::Connection
@ -242,6 +243,7 @@ public:
{
return connect(sender, signal, sender, std::forward<Func2>(slot), Qt::DirectConnection);
}
#endif // QT_NO_CONTEXTLESS_CONNECT
#endif //Q_QDOC
static bool disconnect(const QObject *sender, const char *signal,