QObject: optimize the common case of findChildren(QString())

Outside tests, all in-tree callers of QObject::findChildren() pass no
name to match, and in my experience that is also true for the vast
majority of out-of-tree users.

Avoid the temporary QString creation in the caller and the repeated
QString::isNull() checks in the implementation by overloading
findChildren() without a name argument and checking for name.isNull()
only once, forking off into separate helper functions.

Adjust in-tree callers that used an explicit `QString()` argument in
order to pass options, which goes to show that `name` should never
have been the first argument of findChilden() in the first place, even
though I appreciate the symmetry with findChild() (the use-cases of
which, however, are radically different).

Change a `findChildren().size() == 0` call found while scanning for
findChildren() calls to `!findChild()` as a drive-by.

Modernize loops in the various qt_qFindChild{,ren}_helper() overloads
to match how the new code looks.

[ChangeLog][QtCore][QObject] Added findChildren() overload taking no
name (thus optimizing this common case).

Change-Id: Ifc56e5438023d079b40c67f11ae274a3e128ad5e
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Marc Mutz 2021-07-02 08:32:09 +02:00
parent 1edb51a315
commit 2b50c8bec0
7 changed files with 77 additions and 35 deletions

View File

@ -441,7 +441,7 @@ int ArthurStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QWid
void ArthurStyle::polish(QWidget *widget)
{
if (widget->layout() && qobject_cast<QGroupBox *>(widget)) {
if (widget->findChildren<QGroupBox *>().size() == 0) {
if (!widget->findChild<QGroupBox *>()) {
widget->layout()->setSpacing(0);
widget->layout()->setContentsMargins(12, 12, 12, 12);
} else {

View File

@ -449,7 +449,7 @@ QListWidget *list = parentWidget->findChild<QListWidget *>(QString(), Qt::FindDi
//! [43]
QList<QPushButton *> childButtons = parentWidget.findChildren<QPushButton *>(QString(), Qt::FindDirectChildrenOnly);
QList<QPushButton *> childButtons = parentWidget.findChildren<QPushButton *>(Qt::FindDirectChildrenOnly);
//! [43]
//! [44]

View File

@ -1947,7 +1947,8 @@ void QObject::killTimer(int id)
Returns all children of this object with the given \a name that can be
cast to type T, or an empty list if there are no such objects.
Omitting the \a name argument causes all object names to be matched.
A null \a name argument causes all objects to be matched, an empty one
only those whose objectName is empty.
The search is performed recursively, unless \a options specifies the
option FindDirectChildrenOnly.
@ -1967,6 +1968,19 @@ void QObject::killTimer(int id)
\sa findChild()
*/
/*!
\fn template<typename T> QList<T> QObject::findChildren(Qt::FindChildOptions options) const
\overload
\since 6.3
Returns all children of this object that can be cast to type T, or
an empty list if there are no such objects.
The search is performed recursively, unless \a options specifies the
option FindDirectChildrenOnly.
\sa findChild()
*/
/*!
\fn QList<T> QObject::findChildren(const QRegularExpression &re, Qt::FindChildOptions options) const
\overload findChildren()
@ -2012,24 +2026,46 @@ void QObject::killTimer(int id)
\sa QObject::findChildren()
*/
static void qt_qFindChildren_with_name(const QObject *parent, const QString &name,
const QMetaObject &mo, QList<void *> *list,
Qt::FindChildOptions options)
{
Q_ASSERT(parent);
Q_ASSERT(list);
Q_ASSERT(!name.isNull());
for (QObject *obj : parent->children()) {
if (mo.cast(obj) && obj->objectName() == name)
list->append(obj);
if (options & Qt::FindChildrenRecursively)
qt_qFindChildren_with_name(obj, name, mo, list, options);
}
}
/*!
\internal
*/
void qt_qFindChildren_helper(const QObject *parent, const QString &name,
const QMetaObject &mo, QList<void*> *list, Qt::FindChildOptions options)
{
if (name.isNull())
return qt_qFindChildren_helper(parent, mo, list, options);
else
return qt_qFindChildren_with_name(parent, name, mo, list, options);
}
/*!
\internal
*/
void qt_qFindChildren_helper(const QObject *parent, const QMetaObject &mo,
QList<void*> *list, Qt::FindChildOptions options)
{
Q_ASSERT(parent);
Q_ASSERT(list);
const QObjectList &children = parent->children();
QObject *obj;
for (int i = 0; i < children.size(); ++i) {
obj = children.at(i);
if (mo.cast(obj)) {
if (name.isNull() || obj->objectName() == name)
list->append(obj);
}
for (QObject *obj : parent->children()) {
if (mo.cast(obj))
list->append(obj);
if (options & Qt::FindChildrenRecursively)
qt_qFindChildren_helper(obj, name, mo, list, options);
qt_qFindChildren_helper(obj, mo, list, options);
}
}
@ -2042,10 +2078,7 @@ void qt_qFindChildren_helper(const QObject *parent, const QRegularExpression &re
{
Q_ASSERT(parent);
Q_ASSERT(list);
const QObjectList &children = parent->children();
QObject *obj;
for (int i = 0; i < children.size(); ++i) {
obj = children.at(i);
for (QObject *obj : parent->children()) {
if (mo.cast(obj)) {
QRegularExpressionMatch m = re.match(obj->objectName());
if (m.hasMatch())
@ -2063,18 +2096,13 @@ void qt_qFindChildren_helper(const QObject *parent, const QRegularExpression &re
QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo, Qt::FindChildOptions options)
{
Q_ASSERT(parent);
const QObjectList &children = parent->children();
QObject *obj;
int i;
for (i = 0; i < children.size(); ++i) {
obj = children.at(i);
for (QObject *obj : parent->children()) {
if (mo.cast(obj) && (name.isNull() || obj->objectName() == name))
return obj;
}
if (options & Qt::FindChildrenRecursively) {
for (i = 0; i < children.size(); ++i) {
obj = qt_qFindChild_helper(children.at(i), name, mo, options);
if (obj)
for (QObject *child : parent->children()) {
if (QObject *obj = qt_qFindChild_helper(child, name, mo, options))
return obj;
}
}
@ -3593,7 +3621,7 @@ void QMetaObject::connectSlotsByName(QObject *o)
const QMetaObject *mo = o->metaObject();
Q_ASSERT(mo);
const QObjectList list = // list of all objects to look for matching signals including...
o->findChildren<QObject *>(QString()) // all children of 'o'...
o->findChildren<QObject *>() // all children of 'o'...
<< o; // and the object 'o' itself
// for each method/slot of o ...

View File

@ -83,6 +83,8 @@ typedef QList<QObject*> QObjectList;
Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, const QString &name,
const QMetaObject &mo, QList<void *> *list, Qt::FindChildOptions options);
Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, const QMetaObject &mo,
QList<void *> *list, Qt::FindChildOptions options);
Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, const QRegularExpression &re,
const QMetaObject &mo, QList<void *> *list, Qt::FindChildOptions options);
Q_CORE_EXPORT QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo, Qt::FindChildOptions options);
@ -167,7 +169,7 @@ public:
}
template<typename T>
inline QList<T> findChildren(const QString &aName = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
inline QList<T> findChildren(const QString &aName, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
{
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
QList<T> list;
@ -176,6 +178,16 @@ public:
return list;
}
template<typename T>
QList<T> findChildren(Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
{
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
QList<T> list;
qt_qFindChildren_helper(this, ObjType::staticMetaObject,
reinterpret_cast<QList<void *> *>(&list), options);
return list;
}
#if QT_CONFIG(regularexpression)
template<typename T>
inline QList<T> findChildren(const QRegularExpression &re, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const

View File

@ -2607,7 +2607,7 @@ void QDockAreaLayout::removePlaceHolder(const QString &name)
if (!index.isEmpty())
remove(index);
const auto groups =
mainWindow->findChildren<QDockWidgetGroupWindow *>(QString(), Qt::FindDirectChildrenOnly);
mainWindow->findChildren<QDockWidgetGroupWindow *>(Qt::FindDirectChildrenOnly);
for (QDockWidgetGroupWindow *dwgw : groups) {
index = dwgw->layoutInfo()->indexOfPlaceHolder(name);
if (!index.isEmpty()) {
@ -3045,7 +3045,7 @@ bool QDockAreaLayout::restoreDockWidget(QDockWidget *dockWidget)
{
QDockAreaLayoutItem *item = nullptr;
const auto groups =
mainWindow->findChildren<QDockWidgetGroupWindow *>(QString(), Qt::FindDirectChildrenOnly);
mainWindow->findChildren<QDockWidgetGroupWindow *>(Qt::FindDirectChildrenOnly);
for (QDockWidgetGroupWindow *dwgw : groups) {
QList<int> index = dwgw->layoutInfo()->indexOfPlaceHolder(dockWidget->objectName());
if (!index.isEmpty()) {

View File

@ -431,7 +431,7 @@ void QDockWidgetGroupWindow::destroyOrHideIfEmpty()
}
// Make sure to reparent the possibly floating or hidden QDockWidgets to the parent
const auto dockWidgets = findChildren<QDockWidget *>(QString(), Qt::FindDirectChildrenOnly);
const auto dockWidgets = findChildren<QDockWidget *>(Qt::FindDirectChildrenOnly);
for (QDockWidget *dw : dockWidgets) {
bool wasFloating = dw->isFloating();
bool wasHidden = dw->isHidden();
@ -451,7 +451,7 @@ void QDockWidgetGroupWindow::destroyOrHideIfEmpty()
dw->show();
}
#if QT_CONFIG(tabbar)
const auto tabBars = findChildren<QTabBar *>(QString(), Qt::FindDirectChildrenOnly);
const auto tabBars = findChildren<QTabBar *>(Qt::FindDirectChildrenOnly);
for (QTabBar *tb : tabBars)
tb->setParent(parentWidget());
#endif
@ -1056,7 +1056,7 @@ void QMainWindowLayoutState::saveState(QDataStream &stream) const
dockAreaLayout.saveState(stream);
#if QT_CONFIG(tabbar)
const QList<QDockWidgetGroupWindow *> floatingTabs =
mainWindow->findChildren<QDockWidgetGroupWindow *>(QString(), Qt::FindDirectChildrenOnly);
mainWindow->findChildren<QDockWidgetGroupWindow *>(Qt::FindDirectChildrenOnly);
for (QDockWidgetGroupWindow *floating : floatingTabs) {
if (floating->layoutInfo()->isEmpty())
@ -1835,7 +1835,7 @@ QDockAreaLayoutInfo *QMainWindowLayout::dockInfo(QWidget *widget)
if (info)
return info;
const auto groups =
parent()->findChildren<QDockWidgetGroupWindow*>(QString(), Qt::FindDirectChildrenOnly);
parent()->findChildren<QDockWidgetGroupWindow*>(Qt::FindDirectChildrenOnly);
for (QDockWidgetGroupWindow *dwgw : groups) {
info = dwgw->layoutInfo()->info(widget);
if (info)
@ -2104,7 +2104,7 @@ bool QMainWindowLayout::plug(QLayoutItem *widgetItem)
previousPath = currentHoveredFloat->layoutInfo()->indexOf(widget);
// Let's remove the widget from any possible group window
const auto groups =
parent()->findChildren<QDockWidgetGroupWindow*>(QString(), Qt::FindDirectChildrenOnly);
parent()->findChildren<QDockWidgetGroupWindow*>(Qt::FindDirectChildrenOnly);
for (QDockWidgetGroupWindow *dwgw : groups) {
if (dwgw == currentHoveredFloat)
continue;
@ -2134,7 +2134,7 @@ bool QMainWindowLayout::plug(QLayoutItem *widgetItem)
#if QT_CONFIG(dockwidget)
// Let's remove the widget from any possible group window
const auto groups =
parent()->findChildren<QDockWidgetGroupWindow*>(QString(), Qt::FindDirectChildrenOnly);
parent()->findChildren<QDockWidgetGroupWindow*>(Qt::FindDirectChildrenOnly);
for (QDockWidgetGroupWindow *dwgw : groups) {
QList<int> path = dwgw->layoutInfo()->indexOf(widget);
if (!path.isEmpty())
@ -2730,7 +2730,7 @@ void QMainWindowLayout::applyState(QMainWindowLayoutState &newState, bool animat
#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
QSet<QTabBar*> used = newState.dockAreaLayout.usedTabBars();
const auto groups =
parent()->findChildren<QDockWidgetGroupWindow*>(QString(), Qt::FindDirectChildrenOnly);
parent()->findChildren<QDockWidgetGroupWindow*>(Qt::FindDirectChildrenOnly);
for (QDockWidgetGroupWindow *dwgw : groups)
used += dwgw->layoutInfo()->usedTabBars();

View File

@ -755,6 +755,8 @@ void tst_QObject::findChildren()
op = o.findChild<QObject*>("unnamed", Qt::FindDirectChildrenOnly);
QCOMPARE(op, static_cast<QObject *>(0));
l = o.findChildren<QObject*>(Qt::FindDirectChildrenOnly);
QCOMPARE(l.size(), 5);
l = o.findChildren<QObject*>(QString(), Qt::FindDirectChildrenOnly);
QCOMPARE(l.size(), 5);
l = o.findChildren<QObject*>("", Qt::FindDirectChildrenOnly);