moc: Allow calling ctors on pre-allocated data

When calling a ctor this way, an object is created in the memory pointed
to by _a[0] using the other arguments for the ctor.

This allows separate allocation and initialization of an object through
the metaobject system.

Fixes: QTBUG-108879
Change-Id: Ifb154373ee42faab281cfb62aa14334980ec6b7d
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
bb10
Ulf Hermann 2023-01-31 13:53:44 +01:00
parent b291217457
commit 41248f2590
4 changed files with 68 additions and 17 deletions

View File

@ -107,6 +107,7 @@ using namespace Qt::StringLiterals;
\value RegisterMethodArgumentMetaType
\value BindableProperty
\value CustomCall
\value ConstructInPlace
*/
/*!

View File

@ -521,7 +521,8 @@ struct Q_CORE_EXPORT QMetaObject
RegisterPropertyMetaType,
RegisterMethodArgumentMetaType,
BindableProperty,
CustomCall
CustomCall,
ConstructInPlace,
};
int static_metacall(Call, int, void **) const;

View File

@ -1073,33 +1073,45 @@ void Generator::generateStaticMetacall()
bool needElse = false;
bool isUsed_a = false;
const auto generateCtorArguments = [&](int ctorindex) {
const FunctionDef &f = cdef->constructorList.at(ctorindex);
Q_ASSERT(!f.isPrivateSignal); // That would be a strange ctor indeed
int offset = 1;
int argsCount = f.arguments.size();
for (int j = 0; j < argsCount; ++j) {
const ArgumentDef &a = f.arguments.at(j);
if (j)
fprintf(out, ",");
fprintf(out, "(*reinterpret_cast<%s>(_a[%d]))",
a.typeNameForCast.constData(), offset++);
}
};
if (!cdef->constructorList.isEmpty()) {
fprintf(out, " if (_c == QMetaObject::CreateInstance) {\n");
fprintf(out, " switch (_id) {\n");
for (int ctorindex = 0; ctorindex < cdef->constructorList.size(); ++ctorindex) {
const int ctorend = cdef->constructorList.size();
for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) {
fprintf(out, " case %d: { %s *_r = new %s(", ctorindex,
cdef->classname.constData(), cdef->classname.constData());
const FunctionDef &f = cdef->constructorList.at(ctorindex);
int offset = 1;
int argsCount = f.arguments.size();
for (int j = 0; j < argsCount; ++j) {
const ArgumentDef &a = f.arguments.at(j);
if (j)
fprintf(out, ",");
fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))", a.typeNameForCast.constData(), offset++);
}
if (f.isPrivateSignal) {
if (argsCount > 0)
fprintf(out, ", ");
fprintf(out, "%s", QByteArray("QPrivateSignal()").constData());
}
generateCtorArguments(ctorindex);
fprintf(out, ");\n");
fprintf(out, " if (_a[0]) *reinterpret_cast<%s**>(_a[0]) = _r; } break;\n",
(cdef->hasQGadget || cdef->hasQNamespace) ? "void" : "QObject");
}
fprintf(out, " default: break;\n");
fprintf(out, " }\n");
fprintf(out, " } else if (_c == QMetaObject::ConstructInPlace) {\n");
fprintf(out, " switch (_id) {\n");
for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) {
fprintf(out, " case %d: { new (_a[0]) %s(",
ctorindex, cdef->classname.constData());
generateCtorArguments(ctorindex);
fprintf(out, "); } break;\n");
}
fprintf(out, " default: break;\n");
fprintf(out, " }\n");
fprintf(out, " }");
needElse = true;
isUsed_a = true;

View File

@ -758,6 +758,7 @@ private slots:
void setQPRopertyBinding();
void privateQPropertyShim();
void readWriteThroughBindable();
void invokableCtors();
signals:
void sigWithUnsignedArg(unsigned foo);
@ -4424,6 +4425,42 @@ void tst_Moc::readWriteThroughBindable()
}
}
struct WithInvokableCtor
{
Q_GADGET
Q_PROPERTY(int thing MEMBER m_thing CONSTANT FINAL)
public:
WithInvokableCtor() = default;
Q_INVOKABLE WithInvokableCtor(int theThing) : m_thing(theThing) {}
int m_thing = 10;
};
void tst_Moc::invokableCtors()
{
const QMetaType metaType = QMetaType::fromType<WithInvokableCtor>();
Q_ASSERT(metaType.sizeOf() > 0);
const QMetaObject *metaObject = metaType.metaObject();
QVERIFY(metaObject);
QCOMPARE(metaObject->constructorCount(), 1);
WithInvokableCtor *result = nullptr;
const auto guard = qScopeGuard([&]() { delete result; });
int argument = 17;
void *a[] = { &result, &argument };
metaObject->static_metacall(QMetaObject::CreateInstance, 0, a);
QVERIFY(result);
QCOMPARE(result->m_thing, 17);
// Call dtor so that we're left with "uninitialized" memory.
WithInvokableCtor result2;
result2.~WithInvokableCtor();
void *b[] = { &result2, &argument };
metaObject->static_metacall(QMetaObject::ConstructInPlace, 0, b);
QCOMPARE(result2.m_thing, 17);
}
QTEST_MAIN(tst_Moc)
// the generated code must compile with QT_NO_KEYWORDS