aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2021-10-19 16:54:01 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2021-10-22 10:16:12 +0200
commit6b13c718f8f74642537c7ea36aca884daa25b1ee (patch)
tree2e5b7a46866d2790b5421133441ddfb8ad049800
parentde874252c486d97f4a48eaa11f1f2f0a4c9de382 (diff)
shiboken6: Implement opaque containers for getters (non-const)
Extract helpers from the opaque containers generation for fields and use them for function returns if the type is modified accordingly. [ChangeLog][shiboken6] Getters returning containers by reference can now be modified to return an opaque container by modifying the return type accordingly. Task-number: PYSIDE-1605 Change-Id: Ieaf5eb92d248d3a23e511222e5f61823e85540c0 Reviewed-by: Christian Tismer <tismer@stackless.com> Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io> (cherry picked from commit c0beb9f29f36ea3bc8be26675a05253cc5584fe4)
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafunction.cpp11
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafunction.h2
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetatype.cpp36
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetatype.h2
-rw-r--r--sources/shiboken6/doc/typesystem_containers.rst4
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.cpp43
-rw-r--r--sources/shiboken6/tests/libminimal/listuser.cpp5
-rw-r--r--sources/shiboken6/tests/libminimal/listuser.h1
-rw-r--r--sources/shiboken6/tests/minimalbinding/listuser_test.py6
-rw-r--r--sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml5
10 files changed, 99 insertions, 16 deletions
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
index 2d9ee3dcb..f1bcc3bbb 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
+++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
@@ -784,6 +784,17 @@ QString AbstractMetaFunction::typeReplaced(int key) const
return QString();
}
+bool AbstractMetaFunction::generateOpaqueContainerReturn() const
+{
+ if (d->m_type.typeUsagePattern() != AbstractMetaType::ContainerPattern
+ || d->m_type.referenceType() != LValueReference) {
+ return false;
+ }
+ const QString modifiedReturn = typeReplaced(0);
+ return !modifiedReturn.isEmpty()
+ && d->m_type.generateOpaqueContainerForGetter(modifiedReturn);
+}
+
bool AbstractMetaFunction::isModifiedToArray(int argumentIndex) const
{
for (const auto &modification : modifications(declaringClass())) {
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.h b/sources/shiboken6/ApiExtractor/abstractmetafunction.h
index 415fc4ae2..9001c16e5 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetafunction.h
+++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.h
@@ -311,6 +311,8 @@ public:
QString typeReplaced(int argument_index) const;
bool isModifiedToArray(int argumentIndex) const;
+ bool generateOpaqueContainerReturn() const;
+
/// Return the (modified) type for the signature; modified-pyi-type, modified-type
QString pyiTypeReplaced(int argumentIndex) const;
diff --git a/sources/shiboken6/ApiExtractor/abstractmetatype.cpp b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp
index 8e6238448..84bfa2865 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetatype.cpp
+++ b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp
@@ -58,6 +58,8 @@ public:
QString formatSignature(bool minimal) const;
QString formatPythonSignature() const;
bool equals(const AbstractMetaTypeData &rhs) const;
+ template <class Predicate>
+ bool generateOpaqueContainer(Predicate p) const;
const TypeEntry *m_typeEntry;
AbstractMetaTypeList m_instantiations;
@@ -851,15 +853,16 @@ AbstractMetaType AbstractMetaType::fromAbstractMetaClass(const AbstractMetaClass
return fromTypeEntry(metaClass->typeEntry());
}
-bool AbstractMetaType::generateOpaqueContainer() const
+template <class Predicate> // Predicate(containerTypeEntry, signature)
+bool AbstractMetaTypeData::generateOpaqueContainer(Predicate pred) const
{
- if (!isContainer())
+ if (m_pattern != AbstractMetaType::ContainerPattern)
return false;
- auto *containerTypeEntry = static_cast<const ContainerTypeEntry *>(typeEntry());
+ auto *containerTypeEntry = static_cast<const ContainerTypeEntry *>(m_typeEntry);
auto kind = containerTypeEntry->containerKind();
if (kind != ContainerTypeEntry::ListContainer)
return false;
- const auto &instantation = d->m_instantiations.constFirst();
+ const auto &instantation = m_instantiations.constFirst();
if (instantation.referenceType() != NoReference)
return false;
const QString signature = instantation.cppSignature();
@@ -873,7 +876,7 @@ bool AbstractMetaType::generateOpaqueContainer() const
case TypeEntry::BasicValueType:
case TypeEntry::ObjectType:
case TypeEntry::CustomType:
- result = containerTypeEntry->generateOpaqueContainer(signature);
+ result = pred(containerTypeEntry, signature);
break;
default:
break;
@@ -881,6 +884,29 @@ bool AbstractMetaType::generateOpaqueContainer() const
return result;
}
+// Simple predicate for checking whether an opaque container should be generated
+static bool opaqueContainerPredicate(const ContainerTypeEntry *t,
+ const QString &signature)
+{
+ return t->generateOpaqueContainer(signature);
+}
+
+bool AbstractMetaType::generateOpaqueContainer() const
+{
+ return d->generateOpaqueContainer(opaqueContainerPredicate);
+}
+
+// Helper for determining whether a function should return an opaque container,
+// that is, the function return type is modified accordingly
+// (cf AbstractMetaFunction::generateOpaqueContainerReturn())
+bool AbstractMetaType::generateOpaqueContainerForGetter(const QString &modifiedType) const
+{
+ auto predicate = [&modifiedType](const ContainerTypeEntry *t, const QString &signature) {
+ return t->opaqueContainerName(signature) == modifiedType;
+ };
+ return d->generateOpaqueContainer(predicate);
+}
+
#ifndef QT_NO_DEBUG_STREAM
void AbstractMetaType::formatDebug(QDebug &debug) const
{
diff --git a/sources/shiboken6/ApiExtractor/abstractmetatype.h b/sources/shiboken6/ApiExtractor/abstractmetatype.h
index 98017826e..f248f4252 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetatype.h
+++ b/sources/shiboken6/ApiExtractor/abstractmetatype.h
@@ -249,6 +249,8 @@ public:
bool valueTypeWithCopyConstructorOnlyPassed() const;
/// Returns whether to generate an opaque container for the type
bool generateOpaqueContainer() const;
+ /// Returns whether to generate an opaque container for a getter
+ bool generateOpaqueContainerForGetter(const QString &modifiedType) const;
#ifndef QT_NO_DEBUG_STREAM
void formatDebug(QDebug &debug) const;
diff --git a/sources/shiboken6/doc/typesystem_containers.rst b/sources/shiboken6/doc/typesystem_containers.rst
index d74563b04..ac22df558 100644
--- a/sources/shiboken6/doc/typesystem_containers.rst
+++ b/sources/shiboken6/doc/typesystem_containers.rst
@@ -26,6 +26,10 @@ they are converted to Python containers on read access. By a field modification,
(see :ref:`modify-field`), it is possible to obtain an opaque container
which avoids the conversion and allows for direct modification of elements.
+Getters returning references can also be modified to return opaque containers.
+This is done by modifying the return type to the name of the opaque container
+(see :ref:`replace-type`).
+
The table below lists the functions supported for opaque sequence containers
besides the sequence protocol (element access via index and ``len()``). Both
the STL and the Qt naming convention (which resembles Python's) are supported:
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
index bc1f79e52..3085505db 100644
--- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
@@ -190,6 +190,26 @@ const ProtocolEntries &sequenceProtocols()
return result;
}
+// Return name of function to create PyObject wrapping a container
+static QString opaqueContainerCreationFunc(const AbstractMetaType &type)
+{
+ const auto *containerTypeEntry =
+ static_cast<const ContainerTypeEntry *>(type.typeEntry());
+ const auto *instantiationTypeEntry =
+ type.instantiations().constFirst().typeEntry();
+ return u"create"_qs
+ + containerTypeEntry->opaqueContainerName(instantiationTypeEntry->name());
+}
+
+// Write declaration of the function to create PyObject wrapping a container
+static void writeOpaqueContainerCreationFuncDecl(TextStream &s, const QString &name,
+ AbstractMetaType type)
+{
+ type.setReferenceType(NoReference);
+ type.setConstant(false);
+ s << "PyObject *" << name << '(' << type.cppSignature() << "*);\n";
+}
+
CppGenerator::CppGenerator() = default;
QString CppGenerator::fileNameSuffix() const
@@ -3808,17 +3828,23 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr
}
// Convert result
+ const auto funcType = func->type();
if (!func->conversionRule(TypeSystem::TargetLangCode, 0).isEmpty()) {
writeConversionRule(s, func, TypeSystem::TargetLangCode, QLatin1String(PYTHON_RETURN_VAR));
} else if (!isCtor && !func->isInplaceOperator() && !func->isVoid()
&& !func->injectedCodeHasReturnValueAttribution(TypeSystem::TargetLangCode)) {
- s << PYTHON_RETURN_VAR << " = ";
if (func->type().isObjectTypeUsedAsValueType()) {
- s << "Shiboken::Object::newObject("
+ s << PYTHON_RETURN_VAR << " = Shiboken::Object::newObject("
<< cpythonTypeNameExt(func->type().typeEntry())
<< ", " << CPP_RETURN_VAR << ", true, true)";
+ } else if (func->generateOpaqueContainerReturn()) {
+ const QString creationFunc = opaqueContainerCreationFunc(funcType);
+ writeOpaqueContainerCreationFuncDecl(s, creationFunc, funcType);
+ s << PYTHON_RETURN_VAR << " = " << creationFunc
+ << "(&" << CPP_RETURN_VAR << ");\n";
} else {
- writeToPythonConversion(s, func->type(), func->ownerClass(), QLatin1String(CPP_RETURN_VAR));
+ s << PYTHON_RETURN_VAR << " = ";
+ writeToPythonConversion(s, funcType, func->ownerClass(), QLatin1String(CPP_RETURN_VAR));
}
s << ";\n";
}
@@ -4728,14 +4754,9 @@ void CppGenerator::writeGetterFunction(TextStream &s,
if (metaField.generateOpaqueContainer()
&& fieldType.generateOpaqueContainer()) {
- const auto *containerTypeEntry =
- static_cast<const ContainerTypeEntry *>(fieldType.typeEntry());
- const auto *instantiationTypeEntry =
- fieldType.instantiations().constFirst().typeEntry();
- const QString creationFunc =
- u"create"_qs + containerTypeEntry->opaqueContainerName(instantiationTypeEntry->name());
- s << "PyObject *" << creationFunc << '(' << fieldType.cppSignature() << "*);\n"
- << "PyObject *pyOut = " << creationFunc
+ const QString creationFunc = opaqueContainerCreationFunc(fieldType);
+ writeOpaqueContainerCreationFuncDecl(s, creationFunc, fieldType);
+ s << "PyObject *pyOut = " << creationFunc
<< "(&" << cppField << ");\nPy_IncRef(pyOut);\n"
<< "return pyOut;\n" << outdent << "}\n";
return;
diff --git a/sources/shiboken6/tests/libminimal/listuser.cpp b/sources/shiboken6/tests/libminimal/listuser.cpp
index 402696acd..5ad570faa 100644
--- a/sources/shiboken6/tests/libminimal/listuser.cpp
+++ b/sources/shiboken6/tests/libminimal/listuser.cpp
@@ -123,3 +123,8 @@ void ListUser::setStdIntList(const std::list<int> &l)
{
m_stdIntList = l;
}
+
+std::list<int> &ListUser::getIntList()
+{
+ return m_stdIntList;
+}
diff --git a/sources/shiboken6/tests/libminimal/listuser.h b/sources/shiboken6/tests/libminimal/listuser.h
index 31c4efbd1..e3b38049c 100644
--- a/sources/shiboken6/tests/libminimal/listuser.h
+++ b/sources/shiboken6/tests/libminimal/listuser.h
@@ -71,6 +71,7 @@ struct LIBMINIMAL_API ListUser
int callSumListOfIntLists(std::list<std::list<int> > intListList) { return sumListOfIntLists(intListList); }
void setStdIntList(const std::list<int> &l);
+ std::list<int> &getIntList();
std::list<int> m_stdIntList;
};
diff --git a/sources/shiboken6/tests/minimalbinding/listuser_test.py b/sources/shiboken6/tests/minimalbinding/listuser_test.py
index 1a11ccfbf..7bb65d359 100644
--- a/sources/shiboken6/tests/minimalbinding/listuser_test.py
+++ b/sources/shiboken6/tests/minimalbinding/listuser_test.py
@@ -345,6 +345,12 @@ class ListOfIntListConversionTest(unittest.TestCase):
self.assertEqual(len(lu.m_stdIntList), 3)
self.assertEqual(lu.m_stdIntList[2], 5)
+ # Access list via getter
+ l = lu.getIntList()
+ l.append(6)
+ self.assertEqual(len(lu.m_stdIntList), 4)
+ self.assertEqual(lu.m_stdIntList[3], 6)
+
if __name__ == '__main__':
unittest.main()
diff --git a/sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml b/sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml
index 2b9cbc89f..ce67b1be7 100644
--- a/sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml
+++ b/sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml
@@ -50,6 +50,11 @@
</value-type>
<value-type name="ListUser">
<modify-field name="m_stdIntList" opaque-container="yes"/>
+ <modify-function signature="getIntList()">
+ <modify-argument index="return">
+ <replace-type modified-type="StdIntList"/>
+ </modify-argument>
+ </modify-function>
</value-type>
<value-type name="MinBoolUser"/>