diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2021-10-20 13:51:08 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2021-10-21 13:03:11 +0200 |
commit | e70fbd8d91af1fa9813bc1df7dfad44b1665957e (patch) | |
tree | 459fdff3bb60d2405bd5d0a02292a44b358689d7 | |
parent | c0beb9f29f36ea3bc8be26675a05253cc5584fe4 (diff) |
shiboken6: Implement opaque containers for getters returning a const reference
Add a bool m_const member to the container helper template that
indicates a const container. Error out of the modification functions
if that is set. Create an additional creation function for the const
case. A const opaque containers is then of same Python type as the
non-const version, requiring no further type checks.
Pick-to: 6.2
Task-number: PYSIDE-1605
Change-Id: I45faeb0d68e6144a9dfbe25497694b8acdd98c09
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
7 files changed, 84 insertions, 16 deletions
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index a6abeb077..06299a2ab 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -197,8 +197,11 @@ static QString opaqueContainerCreationFunc(const AbstractMetaType &type) static_cast<const ContainerTypeEntry *>(type.typeEntry()); const auto *instantiationTypeEntry = type.instantiations().constFirst().typeEntry(); - return u"create"_qs - + containerTypeEntry->opaqueContainerName(instantiationTypeEntry->name()); + QString result = u"create"_qs; + if (type.isConstant()) + result += u"Const"_qs; + result += containerTypeEntry->opaqueContainerName(instantiationTypeEntry->name()); + return result; } // Write declaration of the function to create PyObject wrapping a container @@ -206,7 +209,7 @@ static void writeOpaqueContainerCreationFuncDecl(TextStream &s, const QString &n AbstractMetaType type) { type.setReferenceType(NoReference); - type.setConstant(false); + // Maintain const s << "PyObject *" << name << '(' << type.cppSignature() << "*);\n"; } diff --git a/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp b/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp index bc29e1d1e..eab57bcc0 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp @@ -73,6 +73,35 @@ static void writeSlot(TextStream &s, const QString &privateObjType, << "::" << methodName << ")},\n"; } +// Write creation function from C++ reference, used by field accessors +// and getters which are within extern "C" +static void writeContainerCreationFunc(TextStream &s, + const QString &funcName, + const QString &typeFName, + const QString &containerSignature, + bool isConst = false) +{ + + // creation function from C++ reference, used by field accessors + // which are within extern "C" + s << "extern \"C\" PyObject *" << funcName << '('; + if (isConst) + s << "const "; + s << containerSignature << "* ct)\n{\n" << indent + << "auto *container = PyObject_New(ShibokenContainer, " << typeFName << "());\n" + << "auto *d = new ShibokenSequenceContainerPrivate<" + << containerSignature << ">();\n"; + if (isConst) { + s << "d->m_list = const_cast<" << containerSignature << " *>(ct);\n" + << "d->m_const = true;\n"; + } else { + s << "d->m_list = ct;\n"; + } + s << "container->d = d;\n"; + s << "return reinterpret_cast<PyObject *>(container);\n" << outdent + << "}\n\n"; +} + // Generate code for a type wrapping a C++ container instantiation CppGenerator::OpaqueContainerData CppGenerator::writeOpaqueContainerConverterFunctions(TextStream &s, @@ -184,17 +213,11 @@ CppGenerator::OpaqueContainerData << "static PyTypeObject *type = " << typeCreationFName << "();\nreturn type;\n" << outdent << "}\n\n"; - // creation function from C++ reference, used by field accessors - // which are within extern "C" - const QString creationFunctionName = u"create"_qs + result.name; - s << "extern \"C\" PyObject *" << creationFunctionName - << '(' << containerType.cppSignature() << "*ct)\n{\n" << indent - << "auto *container = PyObject_New(ShibokenContainer, " << typeFName << "());\n" - << "auto *d = new ShibokenSequenceContainerPrivate<" - << containerType.cppSignature() << ">();\n" - << "d->m_list = ct;\ncontainer->d = d;\n" - << "return reinterpret_cast<PyObject *>(container);\n" << outdent - << "}\n\n"; + // creation functions from C++ references + writeContainerCreationFunc(s, u"create"_qs + result.name, typeFName, + containerType.cppSignature()); + writeContainerCreationFunc(s, u"createConst"_qs + result.name, typeFName, + containerType.cppSignature(), true); // Check function result.checkFunctionName = result.name + u"_Check"_qs; diff --git a/sources/shiboken6/libshiboken/sbkcontainer.h b/sources/shiboken6/libshiboken/sbkcontainer.h index 284bf8c5c..97062a198 100644 --- a/sources/shiboken6/libshiboken/sbkcontainer.h +++ b/sources/shiboken6/libshiboken/sbkcontainer.h @@ -76,6 +76,9 @@ public: SequenceContainer *m_list{}; bool m_ownsList = false; + bool m_const = false; + static constexpr const char *msgModifyConstContainer = + "Attempt to modify a constant container."; static PyObject *tpNew(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */) { @@ -137,12 +140,16 @@ public: static PyObject *push_back(PyObject *self, PyObject *pyArg) { + auto *d = get(self); if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) { PyErr_SetString(PyExc_TypeError, "wrong type passed to append."); return nullptr; } + if (d->m_const) { + PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); + return nullptr; + } - auto *d = get(self); OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg); if (!value.has_value()) return nullptr; @@ -152,12 +159,16 @@ public: static PyObject *push_front(PyObject *self, PyObject *pyArg) { + auto *d = get(self); if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) { PyErr_SetString(PyExc_TypeError, "wrong type passed to append."); return nullptr; } + if (d->m_const) { + PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); + return nullptr; + } - auto *d = get(self); OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg); if (!value.has_value()) return nullptr; @@ -168,6 +179,11 @@ public: static PyObject *clear(PyObject *self) { auto *d = get(self); + if (d->m_const) { + PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); + return nullptr; + } + d->m_list->clear(); Py_RETURN_NONE; } @@ -175,6 +191,11 @@ public: static PyObject *pop_back(PyObject *self) { auto *d = get(self); + if (d->m_const) { + PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); + return nullptr; + } + d->m_list->pop_back(); Py_RETURN_NONE; } @@ -182,6 +203,11 @@ public: static PyObject *pop_front(PyObject *self) { auto *d = get(self); + if (d->m_const) { + PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); + return nullptr; + } + d->m_list->pop_front(); Py_RETURN_NONE; } diff --git a/sources/shiboken6/tests/libminimal/listuser.cpp b/sources/shiboken6/tests/libminimal/listuser.cpp index 5ad570faa..6a0230d60 100644 --- a/sources/shiboken6/tests/libminimal/listuser.cpp +++ b/sources/shiboken6/tests/libminimal/listuser.cpp @@ -128,3 +128,8 @@ std::list<int> &ListUser::getIntList() { return m_stdIntList; } + +const std::list<int> &ListUser::getConstIntList() const +{ + return m_stdIntList; +} diff --git a/sources/shiboken6/tests/libminimal/listuser.h b/sources/shiboken6/tests/libminimal/listuser.h index e3b38049c..0eae69ae1 100644 --- a/sources/shiboken6/tests/libminimal/listuser.h +++ b/sources/shiboken6/tests/libminimal/listuser.h @@ -72,6 +72,7 @@ struct LIBMINIMAL_API ListUser void setStdIntList(const std::list<int> &l); std::list<int> &getIntList(); + const std::list<int> &getConstIntList() const; std::list<int> m_stdIntList; }; diff --git a/sources/shiboken6/tests/minimalbinding/listuser_test.py b/sources/shiboken6/tests/minimalbinding/listuser_test.py index 7bb65d359..a1fc48c08 100644 --- a/sources/shiboken6/tests/minimalbinding/listuser_test.py +++ b/sources/shiboken6/tests/minimalbinding/listuser_test.py @@ -351,6 +351,11 @@ class ListOfIntListConversionTest(unittest.TestCase): self.assertEqual(len(lu.m_stdIntList), 4) self.assertEqual(lu.m_stdIntList[3], 6) + # Access a const list via getter and verify that it cannot be modified + const_l = lu.getConstIntList() + self.assertEqual(len(const_l), 4) + self.assertRaises(TypeError, const_l.append, 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 94ec4a649..9bb7e7b7e 100644 --- a/sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml +++ b/sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml @@ -53,6 +53,11 @@ <replace-type modified-type="StdIntList"/> </modify-argument> </modify-function> + <modify-function signature="getConstIntList()const"> + <modify-argument index="return"> + <replace-type modified-type="StdIntList"/> + </modify-argument> + </modify-function> </value-type> <value-type name="MinBoolUser"/> |