From 3d7265db9075db7b22b241b659f23d392d62d804 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Apr 2020 20:23:28 +0200 Subject: [PATCH] Provide a way of exposing private QProperties with a fake API The API reduces the amount of manual plumbing required to offer a conceptual property through the traditional setter/getter API as well as through QProperty API. Since the latter would require inlining the type and thus making it impossible to add new properties without breaking binary compatibility, this patch introduces a fake API that behaves similar but does not contain the property by value. Change-Id: Ib9bccd867f0e4e36a520e5583ba348e728284253 Reviewed-by: Fabian Kosmale --- src/corelib/kernel/qtmetamacros.h | 32 +++++++ src/tools/moc/generator.cpp | 105 +++++++++++++++++++++++ src/tools/moc/generator.h | 1 + src/tools/moc/keywords.cpp | 61 +++++++------ src/tools/moc/moc.cpp | 78 ++++++++++++++--- src/tools/moc/moc.h | 12 +++ src/tools/moc/token.h | 1 + src/tools/moc/util/generate_keywords.cpp | 1 + tests/auto/tools/moc/tst_moc.cpp | 60 +++++++++++++ 9 files changed, 315 insertions(+), 36 deletions(-) diff --git a/src/corelib/kernel/qtmetamacros.h b/src/corelib/kernel/qtmetamacros.h index 2dcd6948aa..fb89722847 100644 --- a/src/corelib/kernel/qtmetamacros.h +++ b/src/corelib/kernel/qtmetamacros.h @@ -91,6 +91,34 @@ QT_BEGIN_NAMESPACE #define Q_INTERFACES(x) QT_ANNOTATE_CLASS(qt_interfaces, x) #define Q_PROPERTY(...) QT_ANNOTATE_CLASS(qt_property, __VA_ARGS__) #define Q_PRIVATE_PROPERTY(d, text) QT_ANNOTATE_CLASS2(qt_private_property, d, text) +#define Q_PRIVATE_QPROPERTY(accessor, type, name, setter, ...) \ + struct _qt_property_api_##name { \ + type value() const; \ + type operator()() const { return value(); } \ + void setValue(type &&); \ + void setValue(const type &); \ + void operator=(const type &v) { setValue(v); } \ + void operator=(type &&v) { setValue(std::move(v)); } \ + QPropertyBinding setBinding(const QPropertyBinding &); \ + QPropertyBinding setBinding(QPropertyBinding &&); \ + QPropertyBinding operator=(const QPropertyBinding &b) { return setBinding(b); } \ + QPropertyBinding operator=(QPropertyBinding &&b) { return setBinding(std::move(b)); } \ + bool setBinding(const QUntypedPropertyBinding &); \ + template \ + QPropertyBinding setBinding(Functor f, \ + const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) \ + { \ + return setBinding(Qt::makePropertyBinding(f, location)); \ + } \ + bool hasBinding() const; \ + QPropertyBinding binding() const; \ + QPropertyBinding takeBinding(); \ + }; \ + void setter(const type &value); +#define Q_PRIVATE_QPROPERTIES_BEGIN union { +#define Q_PRIVATE_QPROPERTY_IMPL(name) \ + _qt_property_api_##name name; +#define Q_PRIVATE_QPROPERTIES_END }; #ifndef Q_REVISION # define Q_REVISION(...) #endif @@ -211,6 +239,10 @@ private: \ #define Q_INTERFACES(x) Q_INTERFACES(x) #define Q_PROPERTY(text) Q_PROPERTY(text) #define Q_PRIVATE_PROPERTY(d, text) Q_PRIVATE_PROPERTY(d, text) +#define Q_PRIVATE_QPROPERTY(accessor, type, name, setter, ...) Q_PRIVATE_QPROPERTY(accessor, type, name, setter, __VA_ARGS__) +#define Q_PRIVATE_QPROPERTIES_BEGIN +#define Q_PRIVATE_QPROPERTY_IMPL(name) +#define Q_PRIVATE_QPROPERTIES_END #define Q_REVISION(...) Q_REVISION(__VA_ARGS__) #define Q_OVERRIDE(text) Q_OVERRIDE(text) #define Q_ENUMS(x) Q_ENUMS(x) diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index acb7cdffe9..976d49ad20 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -615,6 +615,11 @@ void Generator::generateCode() for (int signalindex = 0; signalindex < cdef->signalList.size(); ++signalindex) generateSignal(&cdef->signalList[signalindex], signalindex); +// +// Generate QProperty forwarding API +// + generateQPropertyApi(); + // // Generate plugin meta data // @@ -1621,6 +1626,106 @@ void Generator::generateSignal(FunctionDef *def,int index) fprintf(out, "}\n"); } +void Generator::generateQPropertyApi() +{ + for (const PrivateQPropertyDef &property: cdef->privateQProperties) { + auto printAccessor = [this, property](bool constAccessor = false) { + const char *constOrNot = constAccessor ? "const " : " "; + fprintf(out, " const size_t propertyMemberOffset = reinterpret_cast(&(static_cast<%s *>(nullptr)->%s));\n", cdef->qualified.constData(), property.name.constData()); + fprintf(out, " %sauto *thisPtr = reinterpret_cast<%s%s *>(reinterpret_cast<%schar *>(this) - propertyMemberOffset);\n", constOrNot, constOrNot, cdef->qualified.constData(), constOrNot); + }; + + // property accessor + fprintf(out, "\n%s %s::_qt_property_api_%s::value() const\n{\n", + property.type.name.constData(), + cdef->qualified.constData(), + property.name.constData()); + printAccessor(/*const*/true); + fprintf(out, " return thisPtr->%s->%s.value();\n", property.accessor.constData(), property.name.constData()); + fprintf(out, "}\n"); + + // property value setter + fprintf(out, "\nvoid %s::_qt_property_api_%s::setValue(const %s &value)\n{\n", + cdef->qualified.constData(), + property.name.constData(), + property.type.name.constData()); + printAccessor(); + fprintf(out, " return thisPtr->%s->%s.setValue(value);\n", property.accessor.constData(), property.name.constData()); + fprintf(out, "}\n"); + + // property value move setter + fprintf(out, "\nvoid %s::_qt_property_api_%s::setValue(%s &&value)\n{\n", + cdef->qualified.constData(), + property.name.constData(), + property.type.name.constData()); + printAccessor(); + fprintf(out, " return thisPtr->%s->%s.setValue(std::move(value));\n", property.accessor.constData(), property.name.constData()); + fprintf(out, "}\n"); + + // binding setter + fprintf(out, "\nQPropertyBinding<%s> %s::_qt_property_api_%s::setBinding(const QPropertyBinding<%s> &binding)\n{\n", + property.type.name.constData(), + cdef->qualified.constData(), + property.name.constData(), + property.type.name.constData()); + printAccessor(); + fprintf(out, " return thisPtr->%s->%s.setBinding(binding);\n", property.accessor.constData(), property.name.constData()); + fprintf(out, "}\n"); + + // binding move setter + fprintf(out, "\nQPropertyBinding<%s> %s::_qt_property_api_%s::setBinding(QPropertyBinding<%s> &&binding)\n{\n", + property.type.name.constData(), + cdef->qualified.constData(), + property.name.constData(), + property.type.name.constData()); + printAccessor(); + fprintf(out, " return thisPtr->%s->%s.setBinding(std::move(binding));\n", property.accessor.constData(), property.name.constData()); + fprintf(out, "}\n"); + + // untyped binding setter + fprintf(out, "\nbool %s::_qt_property_api_%s::setBinding(const QUntypedPropertyBinding &binding)\n{\n", + cdef->qualified.constData(), + property.name.constData()); + printAccessor(); + fprintf(out, " return thisPtr->%s->%s.setBinding(binding);\n", property.accessor.constData(), property.name.constData()); + fprintf(out, "}\n"); + + // binding bool getter + fprintf(out, "\nbool %s::_qt_property_api_%s::hasBinding() const\n{\n", + cdef->qualified.constData(), + property.name.constData()); + printAccessor(/*const*/true); + fprintf(out, " return thisPtr->%s->%s.hasBinding();\n", property.accessor.constData(), property.name.constData()); + fprintf(out, "}\n"); + + // binding getter + fprintf(out, "\nQPropertyBinding<%s> %s::_qt_property_api_%s::binding() const\n{\n", + property.type.name.constData(), + cdef->qualified.constData(), + property.name.constData()); + printAccessor(/*const*/true); + fprintf(out, " return thisPtr->%s->%s.binding();\n", property.accessor.constData(), property.name.constData()); + fprintf(out, "}\n"); + + // binding taker + fprintf(out, "\nQPropertyBinding<%s> %s::_qt_property_api_%s::takeBinding()\n{\n", + property.type.name.constData(), + cdef->qualified.constData(), + property.name.constData()); + printAccessor(); + fprintf(out, " return thisPtr->%s->%s.takeBinding();\n", property.accessor.constData(), property.name.constData()); + fprintf(out, "}\n"); + + // property setter function + fprintf(out, "\nvoid %s::%s(const %s &value)\n{\n", + cdef->qualified.constData(), + property.setter.constData(), + property.type.name.constData()); + fprintf(out, " %s->%s.setValue(value);\n", property.accessor.constData(), property.name.constData()); + fprintf(out, "}\n\n"); + } +} + static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v); static CborError jsonObjectToCbor(CborEncoder *parent, const QJsonObject &o) { diff --git a/src/tools/moc/generator.h b/src/tools/moc/generator.h index eae0353199..e92b9d1208 100644 --- a/src/tools/moc/generator.h +++ b/src/tools/moc/generator.h @@ -58,6 +58,7 @@ private: void generateMetacall(); void generateStaticMetacall(); void generateSignal(FunctionDef *def, int index); + void generateQPropertyApi(); void generatePluginMetaData(); QMultiMap automaticPropertyMetaTypesHelper(); QMap > methodsWithAutomaticTypesHelper(const QVector &methodList); diff --git a/src/tools/moc/keywords.cpp b/src/tools/moc/keywords.cpp index cc7d747f5b..6a56d2cea5 100644 --- a/src/tools/moc/keywords.cpp +++ b/src/tools/moc/keywords.cpp @@ -30,12 +30,12 @@ // DO NOT EDIT. static const short keyword_trans[][128] = { - {0,0,0,0,0,0,0,0,0,579,576,0,0,0,0,0, + {0,0,0,0,0,0,0,0,0,588,585,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 579,252,577,580,8,38,239,578,25,26,236,234,30,235,27,237, + 588,252,586,589,8,38,239,587,25,26,236,234,30,235,27,237, 22,22,22,22,22,22,22,22,22,22,34,41,23,39,24,43, 0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,21,8,8,8,8,8,8,8,8,8,31,582,32,238,8, + 8,21,8,8,8,8,8,8,8,8,8,31,591,32,238,8, 0,1,2,3,4,5,6,7,8,9,8,8,10,11,12,13, 14,8,15,16,17,18,19,20,8,8,8,36,245,37,248,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -177,7 +177,7 @@ static const short keyword_trans[][128] = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,42,0,0,0,28,0, - 585,585,585,585,585,585,585,585,585,585,0,0,0,0,0,0, + 594,594,594,594,594,594,594,594,594,594,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -336,7 +336,7 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,584,0,0,0,0,583, + 0,0,0,0,0,0,0,0,0,0,593,0,0,0,0,592, 0,0,0,0,0,0,0,0,0,0,0,0,0,258,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -378,8 +378,8 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,475,424,408,416,380,0,484,0,0,0,565,364,358, - 386,0,557,472,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,475,424,408,416,380,0,484,0,0,0,574,364,358, + 386,0,566,472,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -443,7 +443,7 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 549,0,0,517,0,0,0,0,0,0,0,0,0,0,0,0, + 549,557,0,517,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }; @@ -1013,30 +1013,39 @@ static const struct {CHARACTER, 0, 84, 555, CHARACTER}, {CHARACTER, 0, 89, 556, CHARACTER}, {Q_PRIVATE_PROPERTY_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 69, 558, CHARACTER}, - {CHARACTER, 0, 86, 559, CHARACTER}, - {CHARACTER, 0, 73, 560, CHARACTER}, - {CHARACTER, 0, 83, 561, CHARACTER}, - {CHARACTER, 0, 73, 562, CHARACTER}, - {CHARACTER, 0, 79, 563, CHARACTER}, - {CHARACTER, 0, 78, 564, CHARACTER}, - {Q_REVISION_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 79, 566, CHARACTER}, - {CHARACTER, 0, 67, 567, CHARACTER}, - {CHARACTER, 0, 95, 568, CHARACTER}, + {CHARACTER, 0, 80, 558, CHARACTER}, + {CHARACTER, 0, 82, 559, CHARACTER}, + {CHARACTER, 0, 79, 560, CHARACTER}, + {CHARACTER, 0, 80, 561, CHARACTER}, + {CHARACTER, 0, 69, 562, CHARACTER}, + {CHARACTER, 0, 82, 563, CHARACTER}, + {CHARACTER, 0, 84, 564, CHARACTER}, + {CHARACTER, 0, 89, 565, CHARACTER}, + {Q_PRIVATE_QPROPERTY_TOKEN, 0, 0, 0, CHARACTER}, + {CHARACTER, 0, 69, 567, CHARACTER}, + {CHARACTER, 0, 86, 568, CHARACTER}, {CHARACTER, 0, 73, 569, CHARACTER}, - {CHARACTER, 0, 78, 570, CHARACTER}, - {CHARACTER, 0, 67, 571, CHARACTER}, - {CHARACTER, 0, 76, 572, CHARACTER}, - {CHARACTER, 0, 85, 573, CHARACTER}, - {CHARACTER, 0, 68, 574, CHARACTER}, - {CHARACTER, 0, 69, 575, CHARACTER}, + {CHARACTER, 0, 83, 570, CHARACTER}, + {CHARACTER, 0, 73, 571, CHARACTER}, + {CHARACTER, 0, 79, 572, CHARACTER}, + {CHARACTER, 0, 78, 573, CHARACTER}, + {Q_REVISION_TOKEN, 0, 0, 0, CHARACTER}, + {CHARACTER, 0, 79, 575, CHARACTER}, + {CHARACTER, 0, 67, 576, CHARACTER}, + {CHARACTER, 0, 95, 577, CHARACTER}, + {CHARACTER, 0, 73, 578, CHARACTER}, + {CHARACTER, 0, 78, 579, CHARACTER}, + {CHARACTER, 0, 67, 580, CHARACTER}, + {CHARACTER, 0, 76, 581, CHARACTER}, + {CHARACTER, 0, 85, 582, CHARACTER}, + {CHARACTER, 0, 68, 583, CHARACTER}, + {CHARACTER, 0, 69, 584, CHARACTER}, {Q_MOC_INCLUDE_TOKEN, 0, 0, 0, CHARACTER}, {NEWLINE, 0, 0, 0, NOTOKEN}, {QUOTE, 0, 0, 0, NOTOKEN}, {SINGLEQUOTE, 0, 0, 0, NOTOKEN}, {WHITESPACE, 0, 0, 0, NOTOKEN}, - {HASH, 0, 35, 581, HASH}, + {HASH, 0, 35, 590, HASH}, {PP_HASHHASH, 0, 0, 0, NOTOKEN}, {BACKSLASH, 0, 0, 0, NOTOKEN}, {CPP_COMMENT, 0, 0, 0, NOTOKEN}, diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 2444d0db90..949a2e075a 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -878,6 +878,9 @@ void Moc::parse() case Q_PRIVATE_PROPERTY_TOKEN: parsePrivateProperty(&def); break; + case Q_PRIVATE_QPROPERTY_TOKEN: + parsePrivateQProperty(&def); + break; case ENUM: { EnumDef enumDef; if (parseEnum(&enumDef)) @@ -1031,10 +1034,14 @@ static QByteArrayList requiredQtContainers(const QVector &classes) QByteArrayList required; required.reserve(candidates.size()); + bool needsQProperty = false; + for (const auto &candidate : candidates) { const QByteArray pattern = candidate + '<'; for (const auto &c : classes) { + if (!c.privateQProperties.isEmpty()) + needsQProperty = true; if (any_type_contains(c.propertyList, pattern) || any_arg_contains(c.slotList, pattern) || any_arg_contains(c.signalList, pattern) || @@ -1045,6 +1052,9 @@ static QByteArrayList requiredQtContainers(const QVector &classes) } } + if (needsQProperty) + required.push_back("QProperty"); + return required; } @@ -1268,6 +1278,14 @@ void Moc::createPropertyDef(PropertyDef &propDef) propDef.type = type; + next(); + propDef.name = lexem(); + + parsePropertyAttributes(propDef); +} + +void Moc::parsePropertyAttributes(PropertyDef &propDef) +{ auto checkIsFunction = [&](const QByteArray &def, const char *name) { if (def.endsWith(')')) { QByteArray msg = "Providing a function for "; @@ -1277,9 +1295,6 @@ void Moc::createPropertyDef(PropertyDef &propDef) } }; - next(); - propDef.name = lexem(); - while (test(IDENTIFIER)) { const QByteArray l = lexem(); if (l[0] == 'C' && l == "CONSTANT") { @@ -1459,23 +1474,30 @@ void Moc::parsePluginData(ClassDef *def) next(RPAREN); } -void Moc::parsePrivateProperty(ClassDef *def) +QByteArray Moc::parsePropertyAccessor() { - next(LPAREN); - PropertyDef propDef; next(IDENTIFIER); - propDef.inPrivateClass = lexem(); + QByteArray accessor = lexem(); while (test(SCOPE)) { - propDef.inPrivateClass += lexem(); + accessor += lexem(); next(IDENTIFIER); - propDef.inPrivateClass += lexem(); + accessor += lexem(); } // also allow void functions if (test(LPAREN)) { next(RPAREN); - propDef.inPrivateClass += "()"; + accessor += "()"; } + return accessor; +} + +void Moc::parsePrivateProperty(ClassDef *def) +{ + next(LPAREN); + PropertyDef propDef; + propDef.inPrivateClass = parsePropertyAccessor(); + next(COMMA); createPropertyDef(propDef); @@ -1488,6 +1510,42 @@ void Moc::parsePrivateProperty(ClassDef *def) def->propertyList += propDef; } +void Moc::parsePrivateQProperty(ClassDef *def) +{ + next(LPAREN); + const QByteArray accessor = parsePropertyAccessor(); + next(COMMA); + const Type type = parseType(); + next(COMMA); + next(IDENTIFIER); + const QByteArray name = lexem(); + next(COMMA); + next(IDENTIFIER); + const QByteArray setter = lexem(); + + def->privateQProperties += PrivateQPropertyDef{type, name, setter, accessor}; + + PropertyDef propDef; + propDef.name = name; + propDef.type = type.name; + propDef.read = name + ".value"; + propDef.write = name + ".setValue"; + propDef.isQProperty = true; + propDef.inPrivateClass = accessor; + propDef.designable = propDef.scriptable = propDef.stored = "true"; + propDef.user = "false"; + + if (test(COMMA)) + parsePropertyAttributes(propDef); + + next(RPAREN); + + if (!propDef.notify.isEmpty()) + def->notifyableProperties++; + + def->propertyList += propDef; +} + void Moc::parseEnumOrFlag(BaseDef *def, bool isFlag) { next(LPAREN); diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index 210b6c7c2a..486d033049 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -148,6 +148,14 @@ struct PropertyDef }; Q_DECLARE_TYPEINFO(PropertyDef, Q_MOVABLE_TYPE); +struct PrivateQPropertyDef +{ + Type type; + QByteArray name; + QByteArray setter; + QByteArray accessor; +}; +Q_DECLARE_TYPEINFO(PrivateQPropertyDef, Q_MOVABLE_TYPE); struct ClassInfoDef { @@ -191,6 +199,7 @@ struct ClassDef : BaseDef { QVector signalList, slotList, methodList, publicList; QVector nonClassSignalList; QVector propertyList; + QVector privateQProperties; QSet qPropertyMembers; int notifyableProperties = 0; int revisionedMethods = 0; @@ -258,6 +267,7 @@ public: void parseProperty(ClassDef *def); void parsePluginData(ClassDef *def); void createPropertyDef(PropertyDef &def); + void parsePropertyAttributes(PropertyDef &propDef); void parseEnumOrFlag(BaseDef *def, bool isFlag); void parseFlag(BaseDef *def); void parseClassInfo(BaseDef *def); @@ -266,7 +276,9 @@ public: void parseDeclareMetatype(); void parseMocInclude(); void parseSlotInPrivate(ClassDef *def, FunctionDef::Access access); + QByteArray parsePropertyAccessor(); void parsePrivateProperty(ClassDef *def); + void parsePrivateQProperty(ClassDef *def); void parseFunctionArguments(FunctionDef *def); diff --git a/src/tools/moc/token.h b/src/tools/moc/token.h index c11ec6a38c..0b6e9c4f7c 100644 --- a/src/tools/moc/token.h +++ b/src/tools/moc/token.h @@ -178,6 +178,7 @@ QT_BEGIN_NAMESPACE F(Q_INVOKABLE_TOKEN) \ F(Q_SCRIPTABLE_TOKEN) \ F(Q_PRIVATE_PROPERTY_TOKEN) \ + F(Q_PRIVATE_QPROPERTY_TOKEN) \ F(Q_REVISION_TOKEN) \ F(Q_MOC_INCLUDE_TOKEN) \ F(SPECIAL_TREATMENT_MARK) \ diff --git a/src/tools/moc/util/generate_keywords.cpp b/src/tools/moc/util/generate_keywords.cpp index c2cfe37fab..02a36c5000 100644 --- a/src/tools/moc/util/generate_keywords.cpp +++ b/src/tools/moc/util/generate_keywords.cpp @@ -242,6 +242,7 @@ static const Keyword keywords[] = { { "Q_SLOT", "Q_SLOT_TOKEN" }, { "Q_SCRIPTABLE", "Q_SCRIPTABLE_TOKEN" }, { "Q_PRIVATE_PROPERTY", "Q_PRIVATE_PROPERTY_TOKEN" }, + { "Q_PRIVATE_QPROPERTY", "Q_PRIVATE_QPROPERTY_TOKEN" }, { "Q_REVISION", "Q_REVISION_TOKEN" }, { "Q_MOC_INCLUDE", "Q_MOC_INCLUDE_TOKEN" }, { "\n", "NEWLINE" }, diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index f3cc21f502..2fe5f42609 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -727,6 +727,7 @@ private slots: void qpropertyMembers(); void observerMetaCall(); void setQPRopertyBinding(); + void privateQPropertyShim(); signals: void sigWithUnsignedArg(unsigned foo); @@ -4212,6 +4213,65 @@ void tst_Moc::setQPRopertyBinding() QVERIFY(bindingCalled); // but now it should've been called :) } + +class ClassWithPrivateQPropertyShim :public QObject +{ + Q_OBJECT +public: + Q_PRIVATE_QPROPERTY(d_func(), int, testProperty, setTestProperty, NOTIFY testPropertyChanged) + + Q_PRIVATE_QPROPERTIES_BEGIN + Q_PRIVATE_QPROPERTY_IMPL(testProperty) + Q_PRIVATE_QPROPERTIES_END + +signals: + void testPropertyChanged(); +public: + + struct Private { + Private(ClassWithPrivateQPropertyShim *pub) + : q(pub) + {} + + ClassWithPrivateQPropertyShim *q = nullptr; + + QProperty testProperty; + void onTestPropertyChanged() { q->testPropertyChanged(); } + QPropertyMemberChangeHandler<&Private::testProperty, &Private::onTestPropertyChanged> testChangeHandler{this}; + }; + Private priv{this}; + + Private *d_func() { return &priv; } + const Private *d_func() const { return &priv; } +}; + + +void tst_Moc::privateQPropertyShim() +{ + ClassWithPrivateQPropertyShim testObject; + + { + auto metaObject = &ClassWithPrivateQPropertyShim::staticMetaObject; + QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("testProperty")); + QVERIFY(prop.isValid()); + QVERIFY(prop.notifySignal().isValid()); + } + + testObject.priv.testProperty.setValue(42); + QCOMPARE(testObject.property("testProperty").toInt(), 42); + + // Behave like a QProperty + QVERIFY(!testObject.testProperty.hasBinding()); + testObject.testProperty.setBinding([]() { return 100; }); + QCOMPARE(testObject.testProperty.value(), 100); + QVERIFY(testObject.testProperty.hasBinding()); + + // Old style setter getters + testObject.setTestProperty(400); + QVERIFY(!testObject.testProperty.hasBinding()); + QCOMPARE(testObject.testProperty(), 400); +} + QTEST_MAIN(tst_Moc) // the generated code must compile with QT_NO_KEYWORDS