You can "cheat" and leverage the properties of the ABI to access the type even without including its header. All we really care for is to declare a variable that will generate the correct mangled name for the CustomWidget::staticMetaObject
.
A class and QObject::findChildren
Per the language standard, this would be UB, but all mainstream ABIs support this 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);
A namespace and findChildren
's implementation detail
This also works fine on all sane compilers, even if per the standard it's 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);
Direct use of mangled name
We can refer directly to the staticMetaObject
's mangled name - this works on GCC, ICC and Clang. Unfortunately, there's no way to implement it on MSVC, due to the mangled names not being valid identifiers. The Length
argument is asserted to be correct at compile time, but unfortunately there is no preprocessor trick to get its length and avoid the need for passing it in.
#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());
Export of of symbols with known names
This solution is a variation of the namespace approach above, but it doesn't involve any UB. It does require additions
// 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)
Then use qt_qFindChildren_helper
as in the 2nd (namespace) solution above.