From ad32ac5b4f05c9eed1fb7a93ee7947050d840a19 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 22 Aug 2020 17:21:36 +0200 Subject: Make bindings introspectable through moc Add a new BINDABLE declaration to the Q_PROPERTY() macro that tells moc where to find the QBindable for the property. Add a QUntypedBindable base class to QBindable that gives access to generic functionality and checks argument compatibility at runtime. QBindable will still do static checking at compile time. Add QMetaProperty::isBindable() and QMetaProperty::bindable() to be able to dynamically access the binding functionality. Change-Id: Ic7b08ae2cde83fd43e627d813a886e1de01fa3dc Reviewed-by: Fabian Kosmale --- src/tools/moc/generator.cpp | 59 ++++++++------------------------------------- src/tools/moc/moc.cpp | 29 +++++++++------------- src/tools/moc/moc.h | 3 +-- 3 files changed, 23 insertions(+), 68 deletions(-) (limited to 'src/tools/moc') diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 1d2fa5d1d7..155a98209d 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -906,8 +906,8 @@ void Generator::generateProperties() if (p.required) flags |= Required; - if (p.isQProperty) - flags |= IsQProperty; + if (!p.bind.isEmpty()) + flags |= Bindable; fprintf(out, " %4d, ", stridx(p.name)); generateTypeInfo(p.type); @@ -1025,9 +1025,8 @@ void Generator::generateMetacall() fprintf(out, "else "); fprintf(out, "if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty\n" - " || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType\n" - " || _c == QMetaObject::RegisterQPropertyObserver\n" - " || _c == QMetaObject::SetQPropertyBinding) {\n" + " || _c == QMetaObject::ResetProperty || _c == QMetaObject::BindableProperty\n" + " || _c == QMetaObject::RegisterPropertyMetaType) {\n" " qt_static_metacall(this, _c, _id, _a);\n" " _id -= %d;\n }", int(cdef->propertyList.count())); fprintf(out, "\n#endif // QT_NO_PROPERTIES"); @@ -1268,7 +1267,7 @@ void Generator::generateStaticMetacall() bool needTempVarForGet = false; bool needSet = false; bool needReset = false; - bool haveQProperties = false; + bool hasBindableProperties = false; for (int i = 0; i < cdef->propertyList.size(); ++i) { const PropertyDef &p = cdef->propertyList.at(i); needGet |= !p.read.isEmpty() || !p.member.isEmpty(); @@ -1278,7 +1277,7 @@ void Generator::generateStaticMetacall() needSet |= !p.write.isEmpty() || (!p.member.isEmpty() && !p.constant); needReset |= !p.reset.isEmpty(); - haveQProperties |= p.isQProperty; + hasBindableProperties |= !p.bind.isEmpty(); } fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); @@ -1404,59 +1403,21 @@ void Generator::generateStaticMetacall() } fprintf(out, " }"); -#if 0 fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::RegisterQPropertyObserver) {\n"); - if (haveQProperties) { + fprintf(out, "if (_c == QMetaObject::BindableProperty) {\n"); + if (hasBindableProperties) { setupMemberAccess(); - fprintf(out, " QPropertyObserver *observer = reinterpret_cast(_a[0]);\n"); fprintf(out, " switch (_id) {\n"); for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { const PropertyDef &p = cdef->propertyList.at(propindex); - if (!p.isQProperty) + if (p.bind.isEmpty()) continue; - QByteArray prefix = "_t->"; - if (p.qpropertyname.isEmpty() || p.stored == "true") { - fprintf(out, " case %d: observer->setSource(%s%s); break;\n", - propindex, prefix.constData(), p.bindingAccessor.constData()); - } else { - fprintf(out, " case %d: if (auto *source = %s%s) observer->setSource(*source); break; \n", - propindex, prefix.constData(), p.bindingAccessor.constData()); - } + fprintf(out, " case %d: *static_cast(_a[0]) = _t->%s(); break;\n", propindex, p.bind.constData()); } fprintf(out, " default: break;\n"); fprintf(out, " }\n"); } fprintf(out, " }"); - - fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::SetQPropertyBinding) {\n"); - if (haveQProperties) { - setupMemberAccess(); - fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { - const PropertyDef &p = cdef->propertyList.at(propindex); - if (!p.isQProperty) - continue; - QByteArray prefix = "_t->"; - - if (p.qpropertyname.isEmpty() || p.stored == "true") { - fprintf(out, " case %d: %s%s.setBinding(*reinterpret_cast *>(_a[0])); break;\n", - propindex, prefix.constData(), - p.bindingAccessor.constData(), - p.type.constData()); - } else { - fprintf(out, " case %d: if (auto *source = %s%s) source->setBinding(*reinterpret_cast *>(_a[0])); break;\n", - propindex, prefix.constData(), p.bindingAccessor.constData(), - p.type.constData()); - } - - } - fprintf(out, " default: break;\n"); - fprintf(out, " }\n"); - } - fprintf(out, " }"); -#endif fprintf(out, "\n#endif // QT_NO_PROPERTIES"); needElse = true; } diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 1a20d32d7c..516b231cd5 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -1022,7 +1022,7 @@ static QByteArrayList requiredQtContainers(const QList &classes) for (const auto &c : classes) { for (const auto &p : c.propertyList) - needsQProperty |= p.isQProperty; + needsQProperty |= !p.bind.isEmpty(); if (any_type_contains(c.propertyList, pattern) || any_arg_contains(c.slotList, pattern) || any_arg_contains(c.signalList, pattern) || @@ -1219,23 +1219,9 @@ void Moc::createPropertyDef(PropertyDef &propDef) { propDef.location = index; - const bool isPrivateProperty = !propDef.inPrivateClass.isEmpty(); - bool typeWrappedInQProperty = false; - if (isPrivateProperty) { - const int rewind = index; - if (test(IDENTIFIER) && lexem() == "QProperty" && test(LANGLE)) { - typeWrappedInQProperty = true; - propDef.isQProperty = true; - } else { - index = rewind; - } - } - QByteArray type = parseType().name; if (type.isEmpty()) error(); - if (typeWrappedInQProperty) - next(RANGLE); propDef.designable = propDef.scriptable = propDef.stored = "true"; propDef.user = "false"; /* @@ -1346,6 +1332,9 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef) case 'W': if (l != "WRITE") error(2); propDef.write = v; break; + case 'B': if (l != "BINDABLE") error(2); + propDef.bind = v; + break; case 'D': if (l != "DESIGNABLE") error(2); propDef.designable = v + v2; checkIsFunction(propDef.designable, "DESIGNABLE"); @@ -1373,6 +1362,12 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef) propDef.constant = false; warning(msg.constData()); } + if (propDef.constant && !propDef.bind.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is both BINDable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } } void Moc::parseProperty(ClassDef *def) @@ -1808,7 +1803,7 @@ void Moc::checkProperties(ClassDef *cdef) warning(msg.constData()); } - if (p.read.isEmpty() && p.member.isEmpty() && !p.isQProperty) { + if (p.read.isEmpty() && p.member.isEmpty() && p.bind.isEmpty()) { const int rewind = index; if (p.location >= 0) index = p.location; @@ -2012,6 +2007,7 @@ QJsonObject PropertyDef::toJson() const jsonify("member", member); jsonify("read", read); jsonify("write", write); + jsonify("bindable", bind); jsonify("reset", reset); jsonify("notify", notify); jsonify("privateClass", inPrivateClass); @@ -2035,7 +2031,6 @@ QJsonObject PropertyDef::toJson() const prop[QLatin1String("constant")] = constant; prop[QLatin1String("final")] = final; prop[QLatin1String("required")] = required; - prop[QLatin1String("isQProperty")] = isQProperty; if (revision > 0) prop[QLatin1String("revision")] = revision; diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index def5a8e82d..7f63402aca 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -132,7 +132,7 @@ struct PropertyDef return (s == write); } - QByteArray name, type, member, read, write, reset, designable, scriptable, stored, user, notify, inPrivateClass; + QByteArray name, type, member, read, write, bind, reset, designable, scriptable, stored, user, notify, inPrivateClass; int notifyId = -1; // -1 means no notifyId, >= 0 means signal defined in this class, < -1 means signal not defined in this class enum Specification { ValueSpec, ReferenceSpec, PointerSpec }; Specification gspec = ValueSpec; @@ -140,7 +140,6 @@ struct PropertyDef bool constant = false; bool final = false; bool required = false; - bool isQProperty = false; int location = -1; // token index, used for error reporting -- cgit v1.2.3