您可以“作弊”并利用 ABI 的属性来访问该类型,即使不包括其标头。我们真正关心的是声明一个变量,该变量将为CustomWidget::staticMetaObject
.
一个班级和QObject::findChildren
根据语言标准,这将是 UB,但所有主流 ABI 都支持这种 hack。
class NoneSpecial { // A base used not to violate ODR
NoneSpecial() = delete;
NoneSpecial(const NoneSpecial &) = delete;
NoneSpecial(NoneSpecial &&) = delete;
void operator=(const NoneSpecial &) = delete;
void operator=(NoneSpecial &&) = delete;
~NoneSpecial() = delete;
};
class CustomWidget final : NoneSpecial { // Must not inherit any other base!
public:
static const QMetaObject staticMetaObject;
};
template <typename T> QList<QWidget*> getWidgetChildren(QWidget *parent,
Qt::FindChildOptions options = Qt::FindChildrenRecursively)
{
auto const widgets = parent->findChildren<T*>();
return reinterpret_cast<const QList<QWidget*>&>(widgets);
}
auto widgets = getWidgetChildren<CustomWidget>(parentWidget);
命名空间和findChildren
实现细节
这也适用于所有理智的编译器,即使按照标准它是 UB。
namespace CustomWidget {
extern const QMetaObject staticMetaObject;
}
QList<QWidget*> getWidgetChildren(QWidget *parent, const QMetaObject & mo,
Qt::FindChildOptions options = Qt::FindChildrenRecursively)
{
QList<QWidget*> widgets;
qt_qFindChildren_helper(parent, {}, mo,
reinterpret_cast<QList<void*>*>(&widgets),
options);
return widgets;
}
auto widgets = getWidgetChildren(parentWidget, CustomWidget::staticMetaObject);
直接使用重命名的名称
我们可以直接引用staticMetaObject
's 的重命名 - 这适用于 GCC、ICC 和 Clang。不幸的是,由于损坏的名称不是有效的标识符,因此无法在 MSVC 上实现它。该Length
参数在编译时被断言为正确的,但不幸的是,没有预处理器技巧来获取它的长度并避免传递它的需要。
#ifdef __GNUG__
// Works on gcc, clang and icc
#define DECLARE_STATIC_METAOBJECT(Class, Length) \
inline const QMetaObject & Class##_staticMetaObject() { \
static_assert(sizeof(#Class) == (Length+1)); \
extern const QMetaObject _ZN##Length##Class##16staticMetaObjectE; \
return _ZN##Length##Class##16staticMetaObjectE; \
}
DECLARE_STATIC_METAOBJECT(CustomWidget, 16)
#endif
auto widgets = getWidgetChildren(parentWidget, CustomWidget_staticMetaObject());
导出已知名称的符号
此解决方案是上述命名空间方法的变体,但不涉及任何 UB。它确实需要添加
// metaexport.h
#define DEFINE_META_EXPORT(Class) \
const QMetaObject & Class#_staticMetaObject() { return Class::staticMetaObject; }
#define DECLARE_META_EXPORT(Class) const QMetaObject & Class#_staticMetaObject();
// customwidget.cpp
#include "customwidget.h"
#include "metaexport.h"
DEFINE_META_EXPORT(CustomWidget)
...
// myclass.cpp
// doesn't include "customwidget.h"
#include "metaexport.h"
DECLARE_META_EXPORT(CustomWidget)
然后qt_qFindChildren_helper
在上面的第二个(命名空间)解决方案中使用。