diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2020-09-02 10:52:18 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2020-09-30 08:21:03 +0000 |
commit | 0f922f604297a2022527f04a696da121396ddc0a (patch) | |
tree | 5a40599046ff6ab940aff6d639530237ce08c4d8 | |
parent | 7af97fa4136d66bbad6c7907de6e7bd823de2e43 (diff) |
Add QStringView/QByteArrayView
View types as function parameters cannot be converted in the standard
way shiboken does it:
QStringView cppArg0;
pythonToCpp(pyArg, &cppArg0);
since they reference some other data.
Introduce a new "viewOn" member to type system entry for them. It
causes the function arguments to be replaced by their viewed-on types
(stringview->string) via metatype.
Add a test in libsample and a test for QUuid::fromString(QStringView).
Test returning QStringView via QRegularExpressionMatch::capturedView().
Task-number: QTBUG-84319
Task-number: PYSIDE-1339
Task-number: PYSIDE-904
Task-number: PYSIDE-487
Change-Id: Iddb4ea268a54928d290e29012e2738772fae83f0
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
20 files changed, 144 insertions, 21 deletions
diff --git a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml index 2cbfe638b..46e3c6fe6 100644 --- a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml +++ b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml @@ -253,6 +253,13 @@ </conversion-rule> </primitive-type> + <primitive-type name="QStringView" target-lang-api-name="PyUnicode" view-on="QString"> + <include file-name="QStringView" location="global"/> + <conversion-rule> + <native-to-target file="../glue/qtcore.cpp" snippet="return-pyunicode"/> + </conversion-rule> + </primitive-type> + <primitive-type name="QChar"> <conversion-rule> <native-to-target file="../glue/qtcore.cpp" snippet="return-pyunicode-qchar"/> @@ -2022,6 +2029,12 @@ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-msetitem"/> </add-function> </value-type> + <primitive-type name="QByteArrayView" view-on="QByteArray" since="6.0"> + <conversion-rule> + <native-to-target file="../glue/qtcore.cpp" snippet="return-pybytes"/> + </conversion-rule> + </primitive-type> + <value-type name="QTextBoundaryFinder"> <enum-type name="BoundaryReason" flags="BoundaryReasons"/> <enum-type name="BoundaryType"/> diff --git a/sources/pyside2/PySide2/glue/qtcore.cpp b/sources/pyside2/PySide2/glue/qtcore.cpp index 45e0e8c25..eb8d6dff4 100644 --- a/sources/pyside2/PySide2/glue/qtcore.cpp +++ b/sources/pyside2/PySide2/glue/qtcore.cpp @@ -1839,6 +1839,10 @@ int usec = PyDateTime_TIME_GET_MICROSECOND(%in); return PyBool_FromLong((bool)%in); // @snippet return-pybool +// @snippet return-pybytes +return PyBytes_FromStringAndSize(%in.constData(), %in.size()); +// @snippet return-pybytes + // @snippet return-pylong return PyLong_FromLong(%in); // @snippet return-pylong diff --git a/sources/pyside2/tests/QtCore/qregularexpression_test.py b/sources/pyside2/tests/QtCore/qregularexpression_test.py index fb2e9c24c..50c755d0e 100644 --- a/sources/pyside2/tests/QtCore/qregularexpression_test.py +++ b/sources/pyside2/tests/QtCore/qregularexpression_test.py @@ -48,6 +48,7 @@ class QRegularExpressionTest(unittest.TestCase): match = re.match('word1 word2 word3') self.assertTrue(match.isValid()) self.assertEqual(match.captured(1), 'word2') + self.assertEqual(match.capturedView(1), 'word2') def testMatchIterator(self): re = QRegularExpression('(\w+)') diff --git a/sources/pyside2/tests/QtCore/quuid_test.py b/sources/pyside2/tests/QtCore/quuid_test.py index da34429f9..ecb4a9562 100644 --- a/sources/pyside2/tests/QtCore/quuid_test.py +++ b/sources/pyside2/tests/QtCore/quuid_test.py @@ -43,7 +43,8 @@ from PySide2.QtCore import QUuid class QUuidTest(unittest.TestCase): def testFromString(self): uuidString = '{fc69b59e-cc34-4436-a43c-ee95d128b8c5}' - uuid = QUuid(uuidString) +# testing overload QUUid::fromString(QStringView) + uuid = QUuid.fromString(uuidString) self.assertTrue(not uuid.isNull()) self.assertEqual(uuid.toString(), uuidString) diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index 899d910bc..d06a338a3 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -1884,6 +1884,17 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio return nullptr; } + // Add view substitution for simple view types of function arguments + // std::string_view -> std::string for foo(std::string_view) + auto viewOnTypeEntry = metaType->typeEntry()->viewOn(); + if (viewOnTypeEntry != nullptr && metaType->indirections() == 0 + && metaType->arrayElementType() == nullptr + && !metaType->hasInstantiations()) { + auto viewOn = new AbstractMetaType(*metaType); + viewOn->setTypeEntry(viewOnTypeEntry); + metaType->setViewOn(viewOn); + } + auto *metaArgument = new AbstractMetaArgument; metaArgument->setType(metaType); diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index 7d98d9ae8..edb449b04 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -194,10 +194,13 @@ AbstractMetaType::AbstractMetaType() : { } +AbstractMetaType::AbstractMetaType(const AbstractMetaType &rhs) = default; + AbstractMetaType::~AbstractMetaType() { qDeleteAll(m_children); m_instantiations.clear(); + delete m_viewOn; } QString AbstractMetaType::package() const @@ -449,6 +452,8 @@ QDebug operator<<(QDebug d, const AbstractMetaType *at) } } d << '>'; + if (at->viewOn()) + d << ", views " << at->viewOn()->name(); } } else { d << '0'; diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index 648c792b3..0e187fbcf 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -303,6 +303,7 @@ public: Q_DECLARE_FLAGS(ComparisonFlags, ComparisonFlag); AbstractMetaType(); + AbstractMetaType(const AbstractMetaType &); ~AbstractMetaType(); QString package() const; @@ -530,6 +531,12 @@ public: bool compare(const AbstractMetaType &rhs, ComparisonFlags = {}) const; + // View on: Type to use for function argument conversion, fex + // std::string_view -> std::string for foo(std::string_view); + // cf TypeEntry::viewOn() + const AbstractMetaType *viewOn() const { return m_viewOn; } + void setViewOn(const AbstractMetaType *v) { m_viewOn = v; } + private: TypeUsagePattern determineUsagePattern() const; QString formatSignature(bool minimal) const; @@ -545,6 +552,7 @@ private: int m_arrayElementCount = -1; const AbstractMetaType *m_arrayElementType = nullptr; const AbstractMetaType *m_originalTemplateType = nullptr; + const AbstractMetaType *m_viewOn = nullptr; Indirections m_indirections; TypeUsagePattern m_pattern = InvalidPattern; @@ -555,8 +563,6 @@ private: ReferenceType m_referenceType = NoReference; AbstractMetaTypeList m_children; - - Q_DISABLE_COPY(AbstractMetaType) }; Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaType::ComparisonFlags); diff --git a/sources/shiboken2/ApiExtractor/messages.cpp b/sources/shiboken2/ApiExtractor/messages.cpp index 55c51ffa4..a660f7e76 100644 --- a/sources/shiboken2/ApiExtractor/messages.cpp +++ b/sources/shiboken2/ApiExtractor/messages.cpp @@ -649,6 +649,12 @@ QString msgIncorrectlyNestedName(const QString &name) + name + QLatin1String(")."); } +QString msgCannotFindView(const QString &viewedName, const QString &name) +{ + return QLatin1String("Unable to find viewed type ") + viewedName + + QLatin1String(" for ") + name; +} + // qtdocgenerator.cpp QString msgTagWarning(const QXmlStreamReader &reader, const QString &context, diff --git a/sources/shiboken2/ApiExtractor/messages.h b/sources/shiboken2/ApiExtractor/messages.h index 6c173dc09..55481270c 100644 --- a/sources/shiboken2/ApiExtractor/messages.h +++ b/sources/shiboken2/ApiExtractor/messages.h @@ -179,6 +179,8 @@ QString msgNoRootTypeSystemEntry(); QString msgIncorrectlyNestedName(const QString &name); +QString msgCannotFindView(const QString &viewedName, const QString &name); + QString msgCyclicDependency(const QString &funcName, const QString &graphName, const QVector<const AbstractMetaFunction *> &involvedConversions); diff --git a/sources/shiboken2/ApiExtractor/typedatabase.cpp b/sources/shiboken2/ApiExtractor/typedatabase.cpp index 6aa5513fa..87f5c49df 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase.cpp +++ b/sources/shiboken2/ApiExtractor/typedatabase.cpp @@ -894,6 +894,8 @@ void TypeEntry::formatDebug(QDebug &d) const FORMAT_BOOL("stream", m_stream) FORMAT_LIST_SIZE("codeSnips", m_codeSnips) FORMAT_NONEMPTY_STRING("conversionRule", m_conversionRule) + if (m_viewOn) + d << ", views=" << m_viewOn->name(); if (!m_version.isNull() && m_version > QVersionNumber(0, 0)) d << ", version=" << m_version; if (m_revision) diff --git a/sources/shiboken2/ApiExtractor/typesystem.h b/sources/shiboken2/ApiExtractor/typesystem.h index fa3eae9fc..00f56c51e 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.h +++ b/sources/shiboken2/ApiExtractor/typesystem.h @@ -863,6 +863,12 @@ public: void setCustomConversion(CustomConversion* customConversion); CustomConversion* customConversion() const; + // View on: Type to use for function argument conversion, fex + // std::string_view -> std::string for foo(std::string_view). + // cf AbstractMetaType::viewOn() + TypeEntry *viewOn() const { return m_viewOn; } + void setViewOn(TypeEntry *v) { m_viewOn = v; } + virtual TypeEntry *clone() const; void useAsTypedef(const TypeEntry *source); @@ -898,6 +904,7 @@ private: CustomConversion *m_customConversion = nullptr; SourceLocation m_sourceLocation; // XML file uint m_codeGeneration = GenerateAll; + TypeEntry *m_viewOn = nullptr; int m_revision = 0; int m_sbkIndex = 0; Type m_type; diff --git a/sources/shiboken2/ApiExtractor/typesystemparser.cpp b/sources/shiboken2/ApiExtractor/typesystemparser.cpp index b71a247f3..7323e01f6 100644 --- a/sources/shiboken2/ApiExtractor/typesystemparser.cpp +++ b/sources/shiboken2/ApiExtractor/typesystemparser.cpp @@ -1112,16 +1112,44 @@ bool TypeSystemParser::checkRootElement() return ok; } -void TypeSystemParser::applyCommonAttributes(const QXmlStreamReader &reader, TypeEntry *type, - QXmlStreamAttributes *attributes) const +static TypeEntry *findViewedType(const QString &name) +{ + const auto range = TypeDatabase::instance()->entries().equal_range(name); + for (auto i = range.first; i != range.second; ++i) { + switch (i.value()->type()) { + case TypeEntry::BasicValueType: + case TypeEntry::PrimitiveType: + case TypeEntry::ContainerType: + case TypeEntry::ObjectType: + return i.value(); + default: + break; + } + } + return nullptr; +} + +bool TypeSystemParser::applyCommonAttributes(const QXmlStreamReader &reader, TypeEntry *type, + QXmlStreamAttributes *attributes) { type->setSourceLocation(SourceLocation(m_currentFile, reader.lineNumber())); type->setCodeGeneration(m_generate); - const int revisionIndex = - indexOfAttribute(*attributes, u"revision"); - if (revisionIndex != -1) - type->setRevision(attributes->takeAt(revisionIndex).value().toInt()); + for (int i = attributes->size() - 1; i >= 0; --i) { + const auto name = attributes->at(i).qualifiedName(); + if (name == u"revision") { + type->setRevision(attributes->takeAt(i).value().toInt()); + } else if (name == u"view-on") { + const QString name = attributes->takeAt(i).value().toString(); + TypeEntry *views = findViewedType(name); + if (views == nullptr) { + m_error = msgCannotFindView(name, type->name()); + return false; + } + type->setViewOn(views); + } + } + return true; } FlagsTypeEntry * @@ -1146,7 +1174,8 @@ FlagsTypeEntry * } ftype->setOriginalName(flagName); - applyCommonAttributes(reader, ftype, attributes); + if (!applyCommonAttributes(reader, ftype, attributes)) + return nullptr; QStringList lst = flagName.split(colonColon()); const QString targetLangFlagName = QStringList(lst.mid(0, lst.size() - 1)).join(QLatin1Char('.')); @@ -1225,7 +1254,8 @@ SmartPointerTypeEntry * auto *type = new SmartPointerTypeEntry(name, getter, smartPointerType, refCountMethodName, since, currentParentTypeEntry()); - applyCommonAttributes(reader, type, attributes); + if (!applyCommonAttributes(reader, type, attributes)) + return nullptr; m_smartPointerInstantiations.insert(type, instantiations); return type; } @@ -1238,7 +1268,8 @@ PrimitiveTypeEntry * if (!checkRootElement()) return nullptr; auto *type = new PrimitiveTypeEntry(name, since, currentParentTypeEntry()); - applyCommonAttributes(reader, type, attributes); + if (!applyCommonAttributes(reader, type, attributes)) + return nullptr; for (int i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); if (name == targetLangNameAttribute()) { @@ -1282,7 +1313,8 @@ ContainerTypeEntry * return nullptr; } auto *type = new ContainerTypeEntry(name, containerType, since, currentParentTypeEntry()); - applyCommonAttributes(reader, type, attributes); + if (!applyCommonAttributes(reader, type, attributes)) + return nullptr; return type; } diff --git a/sources/shiboken2/ApiExtractor/typesystemparser.h b/sources/shiboken2/ApiExtractor/typesystemparser.h index 3eaa74f04..130d8d3d2 100644 --- a/sources/shiboken2/ApiExtractor/typesystemparser.h +++ b/sources/shiboken2/ApiExtractor/typesystemparser.h @@ -169,8 +169,8 @@ private: const TypeEntry *currentParentTypeEntry() const; bool checkRootElement(); - void applyCommonAttributes(const QXmlStreamReader &reader, TypeEntry *type, - QXmlStreamAttributes *attributes) const; + bool applyCommonAttributes(const QXmlStreamReader &reader, TypeEntry *type, + QXmlStreamAttributes *attributes); PrimitiveTypeEntry * parsePrimitiveTypeEntry(const QXmlStreamReader &, const QString &name, const QVersionNumber &since, QXmlStreamAttributes *); diff --git a/sources/shiboken2/doc/typesystem_specifying_types.rst b/sources/shiboken2/doc/typesystem_specifying_types.rst index 221519541..3b40ef236 100644 --- a/sources/shiboken2/doc/typesystem_specifying_types.rst +++ b/sources/shiboken2/doc/typesystem_specifying_types.rst @@ -108,6 +108,7 @@ primitive-type target-name="..." default-constructor="..." preferred-conversion="yes | no" /> + view-on="..." </typesystem> The **name** attribute is the name of the primitive in C++, the optional, @@ -139,6 +140,13 @@ primitive-type used only for classes declared as primitive types and not for primitive C++ types, but that depends on the application using *ApiExtractor*. + The *optional* **view-on** attribute specifies that the type is a view + class like std::string_view or QStringView which has a constructor + accepting another type like std::string or QString. Since typically + no values can be assigned to view classes, no target-to-native conversion + can be generated for them. Instead, an instance of the viewed class should + be instantiated and passed to functions using the view class + for argument types. .. _namespace: diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 8e9b480cc..89e97093b 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -2389,6 +2389,8 @@ void CppGenerator::writeTypeCheck(QTextStream &s, const OverloadData *overloadDa // This condition trusts that the OverloadData object will arrange for // PyInt type to come after the more precise numeric types (e.g. float and bool) const AbstractMetaType *argType = overloadData->argType(); + if (auto viewOn = argType->viewOn()) + argType = viewOn; bool numberType = numericTypes.count() == 1 || ShibokenGenerator::isPyInt(argType); QString customType = (overloadData->hasArgumentTypeReplace() ? overloadData->argumentTypeReplaced() : QString()); bool rejectNull = shouldRejectNullPointerArgument(overloadData->referenceFunction(), overloadData->argPos()); @@ -2419,12 +2421,15 @@ const AbstractMetaType *CppGenerator::getArgumentType(const AbstractMetaFunction return nullptr; } - const AbstractMetaType *argType = nullptr; QString typeReplaced = func->typeReplaced(argPos); - if (typeReplaced.isEmpty()) - argType = (argPos == 0) ? func->type() : func->arguments().at(argPos-1)->type(); - else - argType = buildAbstractMetaTypeFromString(typeReplaced); + if (typeReplaced.isEmpty()) { + if (argPos == 0) + return func->type(); + auto argType = func->arguments().at(argPos - 1)->type(); + return argType->viewOn() ? argType->viewOn() : argType; + } + + auto argType = buildAbstractMetaTypeFromString(typeReplaced); if (!argType && !m_knownPythonTypes.contains(typeReplaced)) { qCWarning(lcShiboken).noquote().nospace() << QString::fromLatin1("Unknown type '%1' used as argument type replacement "\ @@ -4804,7 +4809,10 @@ void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunction args << QLatin1String("self"); const AbstractMetaArgumentList &arguments = f->arguments(); for (const AbstractMetaArgument *arg : arguments) { - QString strArg = arg->type()->pythonSignature(); + const auto *metaType = arg->type(); + if (auto viewOn = metaType->viewOn()) + metaType = viewOn; + QString strArg = metaType->pythonSignature(); if (!arg->defaultValueExpression().isEmpty()) { strArg += QLatin1Char('='); QString e = arg->defaultValueExpression(); diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py index 4af214c8d..7a93fcbe9 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py @@ -255,6 +255,7 @@ type_map.update({ "qreal": float, "QSet": typing.Set, "QString": str, + "QStringView": str, "QStringList": StringList, "quint16": int, "quint32": int, diff --git a/sources/shiboken2/tests/libsample/virtualmethods.cpp b/sources/shiboken2/tests/libsample/virtualmethods.cpp index 8f9c8f64d..705835c0b 100644 --- a/sources/shiboken2/tests/libsample/virtualmethods.cpp +++ b/sources/shiboken2/tests/libsample/virtualmethods.cpp @@ -63,6 +63,11 @@ const Str & VirtualMethods::returnConstRef() const return result; } +int VirtualMethods::stringViewLength(std::string_view in) const +{ + return int(in.size()); +} + double VirtualDaughter2::virtualMethod0(Point pt, int val, Complex cpx, bool b) { return 42 + VirtualMethods::virtualMethod0(pt, val, cpx, b); diff --git a/sources/shiboken2/tests/libsample/virtualmethods.h b/sources/shiboken2/tests/libsample/virtualmethods.h index 4b2d25805..577f6919e 100644 --- a/sources/shiboken2/tests/libsample/virtualmethods.h +++ b/sources/shiboken2/tests/libsample/virtualmethods.h @@ -36,6 +36,9 @@ #include "libsamplemacros.h" #include "strlist.h" +#include <string_view> +#include <string> + class LIBSAMPLE_API VirtualMethods { public: @@ -108,6 +111,8 @@ public: virtual const Str & returnConstRef() const; + virtual int stringViewLength(std::string_view in) const; + protected: // PYSIDE-1388: Protected hack with final classes (see VirtualFinalDaughter). void protectedMethod() {} diff --git a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml index e4963721b..66ae6d0a1 100644 --- a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml @@ -25,6 +25,7 @@ <primitive-type name="long long"/> <primitive-type name="unsigned long long"/> <primitive-type name="std::string"/> + <primitive-type name="std::string_view" view-on="std::string"/> <primitive-type name="ObjectType::Identifier"/> <primitive-type name="Foo::HANDLE" target-lang-api-name="PyLong"/> diff --git a/sources/shiboken2/tests/samplebinding/virtualmethods_test.py b/sources/shiboken2/tests/samplebinding/virtualmethods_test.py index 2d3198071..07b79c489 100644 --- a/sources/shiboken2/tests/samplebinding/virtualmethods_test.py +++ b/sources/shiboken2/tests/samplebinding/virtualmethods_test.py @@ -133,6 +133,11 @@ class VirtualMethodsTest(unittest.TestCase): self.assertTrue(eevd.grand_grand_daughter_name_called) self.assertEqual(eevd.name().prepend(self.prefix_from_codeinjection), name) + def testStringView(self): + virtual_methods = VirtualMethods() + self.assertEqual(virtual_methods.stringViewLength('bla'), 3) + + class PrettyErrorMessageTest(unittest.TestCase): def testIt(self): obj = ExtendedVirtualMethods() |