diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2016-11-18 16:13:28 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2016-12-13 15:08:33 +0000 |
commit | c99c561ee94076c84ddfdf1643a4e50f0a8ce173 (patch) | |
tree | 4e904db456fcbda433c94efaa55f38d1d407f968 /tools | |
parent | 00cf1300fc127d4656f474de46ff845d917b0a26 (diff) |
Optionally generate accessor and signal methods for states
We can easily do this and provide a much nicer API. These methods are
not available for dynamically loaded state machines. By default we
provide the same API for compiled and loaded state machines. The new
methods are only generated if you pass the "--statemethods" parameter
to qscxmlc. A new qmake variable called "QSCXMLC_ARGUMENTS" is added
for any extra arguments to qscxmlc such as this one.
Change-Id: Ie7a4eb4890c9d42f89093f3cf3ea917ef2793518
Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/qscxmlc/decl.t | 5 | ||||
-rw-r--r-- | tools/qscxmlc/doc/qscxmlc.qdoc | 8 | ||||
-rw-r--r-- | tools/qscxmlc/generator.cpp | 36 | ||||
-rw-r--r-- | tools/qscxmlc/generator.h | 3 | ||||
-rw-r--r-- | tools/qscxmlc/moc.h | 4 | ||||
-rw-r--r-- | tools/qscxmlc/qscxmlc.cpp | 4 | ||||
-rw-r--r-- | tools/qscxmlc/scxmlcppdumper.cpp | 64 | ||||
-rw-r--r-- | tools/qscxmlc/scxmlcppdumper.h | 6 |
8 files changed, 117 insertions, 13 deletions
diff --git a/tools/qscxmlc/decl.t b/tools/qscxmlc/decl.t index 9482885..482c123 100644 --- a/tools/qscxmlc/decl.t +++ b/tools/qscxmlc/decl.t @@ -8,6 +8,11 @@ public: ${classname}(QObject *parent = 0); ~${classname}(); +${accessors} + +Q_SIGNALS: +${signals} + private: struct Data; friend struct Data; diff --git a/tools/qscxmlc/doc/qscxmlc.qdoc b/tools/qscxmlc/doc/qscxmlc.qdoc index 07b5c8d..bf38a63 100644 --- a/tools/qscxmlc/doc/qscxmlc.qdoc +++ b/tools/qscxmlc/doc/qscxmlc.qdoc @@ -58,7 +58,8 @@ \section1 Command-Line Options - The \c qscxmlc tool supports the following command-line options: + The \c qscxmlc tool supports the following command-line options, which can be specified using + the \c QSCXMLC_ARGUMENTS variable in the project file: \table \header @@ -83,5 +84,10 @@ \li The class name of the generated state machine. If none is specified, the value of the name attribute of the <scxml> tag is taken. If that attribute is not specified either, the basename (excluding path) is taken from the input file name. + \row + \li \c --statemethods + \li Generate extra accessor and signal methods for states. This way you can connect to + state changes with plain QObject::connect() and directly call a method to find out if + a state is currently active. \endtable */ diff --git a/tools/qscxmlc/generator.cpp b/tools/qscxmlc/generator.cpp index 9082efa..02e41c8 100644 --- a/tools/qscxmlc/generator.cpp +++ b/tools/qscxmlc/generator.cpp @@ -587,6 +587,7 @@ void Generator::generateCode() for (int signalindex = 0; signalindex < cdef->signalList.size(); ++signalindex) generateSignal(&cdef->signalList[signalindex], signalindex); + fprintf(out, "\n"); // // Generate plugin meta data // @@ -1149,7 +1150,7 @@ void Generator::generateStaticMetacall() //---- Changed from the original in moc if (f.implementation) { - fprintf(out, f.implementation, methodindex); + fprintf(out, f.implementation, "_o", methodindex); fprintf(out, " break;\n"); continue; } @@ -1223,7 +1224,7 @@ void Generator::generateStaticMetacall() bool anythingUsed = false; for (int methodindex = 0; methodindex < cdef->signalList.size(); ++methodindex) { const FunctionDef &f = cdef->signalList.at(methodindex); - if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic || f.implementation) + if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic || f.mangledName.isEmpty()) continue; anythingUsed = true; fprintf(out, " {\n"); @@ -1246,8 +1247,9 @@ void Generator::generateStaticMetacall() else fprintf(out, ");\n"); fprintf(out, " if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&%s::%s)) {\n", - cdef->classname.constData(), f.name.constData()); + cdef->classname.constData(), f.mangledName.constData()); fprintf(out, " *result = %d;\n", methodindex); + fprintf(out, " return;\n"); fprintf(out, " }\n }\n"); } if (!anythingUsed) @@ -1521,6 +1523,34 @@ void Generator::generateSignal(FunctionDef *def,int index) fprintf(out, "}\n"); } +void Generator::generateAccessorDefs() +{ + for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + const PropertyDef &p = cdef->propertyList.at(propindex); + if (p.read.isEmpty() || p.mangledName.isEmpty()) + continue; + + fprintf(out, "bool %s::%s() const\n{\n return %s;\n}\n\n", cdef->classname.constData(), + p.mangledName.constData(), p.read.constData()); + } +} + +void Generator::generateSignalDefs() +{ + for (int methodindex = 0; methodindex < cdef->signalList.size(); ++methodindex) { + const FunctionDef &f = cdef->signalList.at(methodindex); + if (!f.implementation || f.mangledName.isEmpty()) + continue; + + fprintf(out, "void %s::%s(bool _t1)\n{\n", cdef->classname.constData(), + f.mangledName.constData()); + fprintf(out, " void *_a[] = { Q_NULLPTR, " + "const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };\n "); + fprintf(out, f.implementation, "this", methodindex); + fprintf(out, "\n}\n\n"); + } +} + #if 0 static void writePluginMetaData(FILE *out, const QJsonObject &data) { diff --git a/tools/qscxmlc/generator.h b/tools/qscxmlc/generator.h index 21d82d3..9109188 100644 --- a/tools/qscxmlc/generator.h +++ b/tools/qscxmlc/generator.h @@ -46,6 +46,9 @@ public: QByteArray> &knownQObjectClasses, const QHash<QByteArray, QByteArray> &knownGadgets, QIODevice &outfile); void generateCode(); + void generateAccessorDefs(); + void generateSignalDefs(); + private: bool registerableMetaType(const QByteArray &propertyType); void registerClassInfoStrings(); diff --git a/tools/qscxmlc/moc.h b/tools/qscxmlc/moc.h index 36cff5f..a2ecb88 100644 --- a/tools/qscxmlc/moc.h +++ b/tools/qscxmlc/moc.h @@ -83,6 +83,7 @@ struct FunctionDef QByteArray normalizedType; QByteArray tag; QByteArray name; + QByteArray mangledName; bool returnTypeIsVolatile; QList<ArgumentDef> arguments; @@ -114,7 +115,8 @@ struct FunctionDef struct PropertyDef { PropertyDef():notifyId(-1), constant(false), final(false), gspec(ValueSpec), revision(0){} - QByteArray name, type, member, read, write, reset, designable, scriptable, editable, stored, user, notify, inPrivateClass; + QByteArray name, mangledName, type, member, read, write, reset, designable, scriptable, + editable, stored, user, notify, inPrivateClass; int notifyId; bool constant; bool final; diff --git a/tools/qscxmlc/qscxmlc.cpp b/tools/qscxmlc/qscxmlc.cpp index 89abdaa..ce45ed0 100644 --- a/tools/qscxmlc/qscxmlc.cpp +++ b/tools/qscxmlc/qscxmlc.cpp @@ -122,6 +122,8 @@ int run(const QStringList &arguments) QCommandLineOption optionClassName(QLatin1String("classname"), QCoreApplication::translate("main", "Generate <name> for state machine class name."), QCoreApplication::translate("main", "name")); + QCommandLineOption optionStateMethods(QLatin1String("statemethods"), + QCoreApplication::translate("main", "Generate read and notify methods for states")); cmdParser.addPositionalArgument(QLatin1String("input"), QCoreApplication::translate("main", "Input SCXML file.")); @@ -130,6 +132,7 @@ int run(const QStringList &arguments) cmdParser.addOption(optionOutputHeaderName); cmdParser.addOption(optionOutputSourceName); cmdParser.addOption(optionClassName); + cmdParser.addOption(optionStateMethods); cmdParser.process(arguments); @@ -149,6 +152,7 @@ int run(const QStringList &arguments) const QString scxmlFileName = inputFiles.at(0); TranslationUnit options; + options.stateMethods = cmdParser.isSet(optionStateMethods); if (cmdParser.isSet(optionNamespace)) options.namespaceName = cmdParser.value(optionNamespace); QString outFileName = cmdParser.value(optionOutputBaseName); diff --git a/tools/qscxmlc/scxmlcppdumper.cpp b/tools/qscxmlc/scxmlcppdumper.cpp index b539e0d..d99b3b0 100644 --- a/tools/qscxmlc/scxmlcppdumper.cpp +++ b/tools/qscxmlc/scxmlcppdumper.cpp @@ -506,6 +506,13 @@ void CppDumper::writeClass(const QString &className, const GeneratedTableData::M Replacements r; r[QStringLiteral("classname")] = className; r[QStringLiteral("properties")] = generatePropertyDecls(info); + if (m_translationUnit->stateMethods) { + r[QStringLiteral("accessors")] = generateAccessorDecls(info); + r[QStringLiteral("signals")] = generateSignalDecls(info); + } else { + r[QStringLiteral("accessors")] = QString(); + r[QStringLiteral("signals")] = QString(); + } genTemplate(h, QStringLiteral(":/decl.t"), r); } @@ -699,8 +706,42 @@ QString CppDumper::generatePropertyDecls(const GeneratedTableData::MetaDataInfo QString decls; for (const QString &stateName : info.stateNames) { - if (!stateName.isEmpty()) + if (stateName.isEmpty()) + continue; + + if (m_translationUnit->stateMethods) { + decls += QString::fromLatin1(" Q_PROPERTY(bool %1 READ %2 NOTIFY %3)\n") + .arg(stateName, mangleIdentifier(stateName), + mangleIdentifier(stateName + QStringLiteral("Changed"))); + } else { decls += QString::fromLatin1(" Q_PROPERTY(bool %1)\n").arg(stateName); + } + } + + return decls; +} + +QString CppDumper::generateAccessorDecls(const GeneratedTableData::MetaDataInfo &info) +{ + QString decls; + + for (const QString &stateName : info.stateNames) { + if (!stateName.isEmpty()) + decls += QString::fromLatin1(" bool %1() const;\n").arg(mangleIdentifier(stateName)); + } + + return decls; +} + +QString CppDumper::generateSignalDecls(const GeneratedTableData::MetaDataInfo &info) +{ + QString decls; + + for (const QString &stateName : info.stateNames) { + if (!stateName.isEmpty()) { + decls += QString::fromLatin1(" void %1(bool);\n") + .arg(mangleIdentifier(stateName + QStringLiteral("Changed"))); + } } return decls; @@ -721,16 +762,18 @@ QString CppDumper::generateMetaObject(const QString &className, if (stateName.isEmpty()) continue; - QByteArray mangledStateName = stateName.toUtf8(); + QByteArray utf8StateName = stateName.toUtf8(); FunctionDef signal; signal.type.name = "void"; signal.type.rawName = signal.type.name; signal.normalizedType = signal.type.name; - signal.name = mangledStateName + "Changed"; + signal.name = utf8StateName + "Changed"; + if (m_translationUnit->stateMethods) + signal.mangledName = mangleIdentifier(stateName + QStringLiteral("Changed")).toUtf8(); signal.access = FunctionDef::Public; signal.isSignal = true; - signal.implementation = "QMetaObject::activate(_o, &staticMetaObject, %d, _a);"; + signal.implementation = "QMetaObject::activate(%s, &staticMetaObject, %d, _a);"; ArgumentDef arg; arg.type.name = "bool"; @@ -744,9 +787,11 @@ QString CppDumper::generateMetaObject(const QString &className, ++classDef.notifyableProperties; PropertyDef prop; prop.name = stateName.toUtf8(); + if (m_translationUnit->stateMethods) + prop.mangledName = mangleIdentifier(stateName).toUtf8(); prop.type = "bool"; prop.read = "isActive(" + QByteArray::number(stateIdx++) + ")"; - prop.notify = mangledStateName + "Changed"; + prop.notify = utf8StateName + "Changed"; prop.notifyId = classDef.signalList.size() - 1; prop.gspec = PropertyDef::ValueSpec; prop.scriptable = "true"; @@ -759,8 +804,13 @@ QString CppDumper::generateMetaObject(const QString &className, QBuffer buf; buf.open(QIODevice::WriteOnly); - Generator(&classDef, QList<QByteArray>(), knownQObjectClasses, - QHash<QByteArray, QByteArray>(), buf).generateCode(); + Generator generator(&classDef, QList<QByteArray>(), knownQObjectClasses, + QHash<QByteArray, QByteArray>(), buf); + generator.generateCode(); + if (m_translationUnit->stateMethods) { + generator.generateAccessorDefs(); + generator.generateSignalDefs(); + } buf.close(); return QString::fromUtf8(buf.buffer()); } diff --git a/tools/qscxmlc/scxmlcppdumper.h b/tools/qscxmlc/scxmlcppdumper.h index 95fa48a..9c8cfe8 100644 --- a/tools/qscxmlc/scxmlcppdumper.h +++ b/tools/qscxmlc/scxmlcppdumper.h @@ -41,12 +41,14 @@ QT_BEGIN_NAMESPACE struct TranslationUnit { TranslationUnit() - : mainDocument(Q_NULLPTR) + : stateMethods(false) + , mainDocument(Q_NULLPTR) {} QString scxmlFileName; QString outHFileName, outCppFileName; QString namespaceName; + bool stateMethods; DocumentModel::ScxmlDocument *mainDocument; QList<DocumentModel::ScxmlDocument *> allDocuments; QHash<DocumentModel::ScxmlDocument *, QString> classnameForDocument; @@ -79,6 +81,8 @@ private: private: QString generatePropertyDecls(const QScxmlInternal::GeneratedTableData::MetaDataInfo &info); + QString generateAccessorDecls(const QScxmlInternal::GeneratedTableData::MetaDataInfo &info); + QString generateSignalDecls(const QScxmlInternal::GeneratedTableData::MetaDataInfo &info); QString generateMetaObject(const QString &className, const QScxmlInternal::GeneratedTableData::MetaDataInfo &info); |