diff options
Diffstat (limited to 'sources/shiboken6/generator/shiboken/cppgenerator_container.cpp')
-rw-r--r-- | sources/shiboken6/generator/shiboken/cppgenerator_container.cpp | 203 |
1 files changed, 112 insertions, 91 deletions
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp b/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp index eab57bcc0..00e0cabea 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp @@ -1,41 +1,21 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cppgenerator.h" +#include "generatorstrings.h" #include <abstractmetalang.h> #include "apiextractorresult.h" #include "ctypenames.h" +#include "containertypeentry.h" #include "textstream.h" +#include "typedatabase.h" #include <QtCore/QDebug> #include <algorithm> +using namespace Qt::StringLiterals; + // Write a PyMethodDef entry, allowing for registering C++ functions // under different names for Python. static void writeMethod(TextStream &s, const QString &privateObjType, @@ -75,23 +55,37 @@ static void writeSlot(TextStream &s, const QString &privateObjType, // Write creation function from C++ reference, used by field accessors // and getters which are within extern "C" + +enum ContainerCreationFlag +{ + None = 0, + Const = 0x1, + Allocate = 0x2 +}; + +Q_DECLARE_FLAGS(ContainerCreationFlags, ContainerCreationFlag) +Q_DECLARE_OPERATORS_FOR_FLAGS(ContainerCreationFlags) + static void writeContainerCreationFunc(TextStream &s, const QString &funcName, const QString &typeFName, const QString &containerSignature, - bool isConst = false) + ContainerCreationFlags flags = {}) { // creation function from C++ reference, used by field accessors // which are within extern "C" s << "extern \"C\" PyObject *" << funcName << '('; - if (isConst) + if (flags.testFlag(ContainerCreationFlag::Const)) s << "const "; s << containerSignature << "* ct)\n{\n" << indent << "auto *container = PyObject_New(ShibokenContainer, " << typeFName << "());\n" << "auto *d = new ShibokenSequenceContainerPrivate<" << containerSignature << ">();\n"; - if (isConst) { + if (flags.testFlag(ContainerCreationFlag::Allocate)) { + s << "d->m_list = new " << containerSignature << "(*ct);\n" + << "d->m_ownsList = true;\n"; + } else if (flags.testFlag(ContainerCreationFlag::Const)) { s << "d->m_list = const_cast<" << containerSignature << " *>(ct);\n" << "d->m_const = true;\n"; } else { @@ -102,34 +96,22 @@ static void writeContainerCreationFunc(TextStream &s, << "}\n\n"; } -// Generate code for a type wrapping a C++ container instantiation -CppGenerator::OpaqueContainerData - CppGenerator::writeOpaqueContainerConverterFunctions(TextStream &s, - const AbstractMetaType &containerType) const +// Generate template specialization of value converter helper +void CppGenerator::writeOpaqueContainerValueConverter(TextStream &s, + const AbstractMetaType &valueType) const { - OpaqueContainerData result; - const auto &valueType = containerType.instantiations().constFirst(); - const auto *containerTypeEntry = static_cast<const ContainerTypeEntry *>(containerType.typeEntry()); - result.name = containerTypeEntry->opaqueContainerName(valueType.typeEntry()->name()); - - const auto cppSignature = containerType.cppSignature(); - s << "\n// Binding for " << cppSignature << "\n\n"; - // Generate template specialization of value converter helper unless it is already there - const QString pyArg = u"pyArg"_qs; - const QString cppArg = u"cppArg"_qs; - const QString valueTypeName = valueType.cppSignature(); const QString checkFunction = cpythonCheckFunction(valueType); s << "template <>\nstruct ShibokenContainerValueConverter<" << valueTypeName << ">\n{\n"; // Type check - s << indent << "static bool checkValue(PyObject *" << pyArg << ")\n{\n" + s << indent << "static bool checkValue(PyObject *" << PYTHON_ARG << ")\n{\n" << indent << "return " << checkFunction; if (!checkFunction.contains(u'(')) s << '('; - s << pyArg << ");\n" + s << PYTHON_ARG << ");\n" << outdent << "}\n\n"; // C++ to Python @@ -141,48 +123,79 @@ CppGenerator::OpaqueContainerData s << valueTypeName << ' '; if (passByConstRef) s << '&'; - s << cppArg << ")\n{\n" << indent << "return "; - writeToPythonConversion(s, valueType, nullptr, cppArg); + s << CPP_ARG << ")\n{\n" << indent << "return "; + writeToPythonConversion(s, valueType, nullptr, CPP_ARG); s << ";\n" << outdent << "}\n\n"; // Python to C++ s << "static std::optional<" << valueTypeName << "> convertValueToCpp(PyObject *" - << pyArg << ")\n{\n" << indent; + << PYTHON_ARG << ")\n{\n" << indent; s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n" << "if (!("; - writeTypeCheck(s, valueType, pyArg), isNumber(valueType.typeEntry()); + writeTypeCheck(s, valueType, PYTHON_ARG), isNumber(valueType.typeEntry()); s << ")) {\n" << indent - << "PyErr_SetString(PyExc_TypeError, \"Wrong type passed to container conversion.\");\n" + << "Shiboken::Errors::setWrongContainerType();\n" << "return {};\n" << outdent << "}\n"; - writePythonToCppTypeConversion(s, valueType, pyArg, cppArg, nullptr, {}); - s << "return " << cppArg << ";\n" << outdent << "}\n" << outdent << "};\n\n"; + writePythonToCppTypeConversion(s, valueType, PYTHON_ARG, CPP_ARG, nullptr, {}); + s << "return " << CPP_ARG << ";\n" << outdent << "}\n" << outdent << "};\n\n"; +} + +// Generate code for a type wrapping a C++ container instantiation +CppGenerator::OpaqueContainerData + CppGenerator::writeOpaqueContainerConverterFunctions(TextStream &s, + const AbstractMetaType &containerType, + QSet<AbstractMetaType> *valueTypes) const +{ + OpaqueContainerData result; + const auto &valueType = containerType.instantiations().constFirst(); + const auto containerTypeEntry = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry()); + result.name = + containerTypeEntry->opaqueContainerName(containerType.instantiationCppSignatures()); + + const auto cppSignature = containerType.cppSignature(); + s << "\n// Binding for " << cppSignature << "\n\n"; - const QString privateObjType = u"ShibokenSequenceContainerPrivate<"_qs + if (!valueTypes->contains(valueType)) { + valueTypes->insert(valueType); + writeOpaqueContainerValueConverter(s, valueType); + } + + const QString privateObjType = u"ShibokenSequenceContainerPrivate<"_s + cppSignature + u'>'; // methods - const bool isStdVector = containerType.name() == u"std::vector"; - const QString methods = result.name + u"_methods"_qs; + const QString &containerName = containerType.name(); + const bool isStdVector = containerName == u"std::vector"; + const auto kind = containerTypeEntry->containerKind(); + const bool isFixed = kind == ContainerTypeEntry::SpanContainer || containerName == u"std::array"; + const QString methods = result.name + u"_methods"_s; s << "static PyMethodDef " << methods << "[] = {\n" << indent; - writeMethod(s, privateObjType, "push_back"); - writeMethod(s, privateObjType, "push_back", "append"); // Qt convention - writeNoArgsMethod(s, privateObjType, "clear"); - writeNoArgsMethod(s, privateObjType, "pop_back"); - writeNoArgsMethod(s, privateObjType, "pop_back", "removeLast"); // Qt convention - if (!isStdVector) { - writeMethod(s, privateObjType, "push_front"); - writeMethod(s, privateObjType, "push_front", "prepend"); // Qt convention - writeNoArgsMethod(s, privateObjType, "pop_front"); - writeMethod(s, privateObjType, "pop_front", "removeFirst"); // Qt convention + if (!isFixed) { + writeMethod(s, privateObjType, "push_back"); + writeMethod(s, privateObjType, "push_back", "append"); // Qt convention + writeNoArgsMethod(s, privateObjType, "clear"); + writeNoArgsMethod(s, privateObjType, "pop_back"); + writeNoArgsMethod(s, privateObjType, "pop_back", "removeLast"); // Qt convention + if (!isStdVector) { + writeMethod(s, privateObjType, "push_front"); + writeMethod(s, privateObjType, "push_front", "prepend"); // Qt convention + writeNoArgsMethod(s, privateObjType, "pop_front"); + writeMethod(s, privateObjType, "pop_front", "removeFirst"); // Qt convention + } + writeMethod(s, privateObjType, "reserve"); // SFINAE'd out for list + writeNoArgsMethod(s, privateObjType, "capacity"); } + writeNoArgsMethod(s, privateObjType, "data"); + writeNoArgsMethod(s, privateObjType, "constData"); s << "{nullptr, nullptr, 0, nullptr} // Sentinel\n" << outdent << "};\n\n"; // slots - const QString slotsList = result.name + u"_slots"_qs; + const QString slotsList = result.name + u"_slots"_s; s << "static PyType_Slot " << slotsList << "[] = {\n" << indent; writeSlot(s, privateObjType, "Py_tp_init", "tpInit"); - writeSlot(s, privateObjType, "Py_tp_new", "tpNew"); + const auto *tpNew = containerTypeEntry->viewOn() == nullptr ? "tpNew" : "tpNewInvalid"; + writeSlot(s, privateObjType, "Py_tp_new", tpNew); writeSlot(s, privateObjType, "Py_tp_free", "tpFree"); writeSlot(s, "Py_tp_dealloc", "Sbk_object_dealloc"); // FIXME? writeSlot(s, "Py_tp_methods", methods.toUtf8().constData()); @@ -192,54 +205,62 @@ CppGenerator::OpaqueContainerData s << "{0, nullptr}\n" << outdent << "};\n\n"; // spec - const QString specName = result.name + u"_spec"_qs; - const QString name = moduleName() + u'.' + result.name; + const QString specName = result.name + u"_spec"_s; + const QString name = TypeDatabase::instance()->defaultPackageName() + + u'.' + result.name; s << "static PyType_Spec " << specName << " = {\n" << indent << "\"" << name.count(u'.') << ':' << name << "\",\n" << "sizeof(ShibokenContainer),\n0,\nPy_TPFLAGS_DEFAULT,\n" << slotsList << outdent << "\n};\n\n"; // type creation function that sets a key in the type dict. - const QString typeCreationFName = u"create"_qs + result.name + u"Type"_qs; + const QString typeCreationFName = u"create"_s + result.name + u"Type"_s; s << "static inline PyTypeObject *" << typeCreationFName << "()\n{\n" << indent - << "static auto *name = Shiboken::PyMagicName::opaque_container();\n" - << "static auto *opaque_entry = Py_BuildValue(\"{O:O}\", name, Py_True);\n" - << "static auto *result = SbkType_FromSpecAddDict(&" << specName << ", opaque_entry);\n" + << "auto *result = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&" + << specName << "));\nPy_INCREF(Py_True);\n" + << "Shiboken::AutoDecRef tpDict(PepType_GetDict(result));\n" + << "PyDict_SetItem(tpDict.object(), " + "Shiboken::PyMagicName::opaque_container(), Py_True);\n" << "return result;\n" << outdent << "}\n\n"; - // typeF() function - const QString typeFName = result.name + u"_TypeF"_qs; + // _TypeF() function + const QString typeFName = result.name + u"_TypeF"_s; s << "static PyTypeObject *" << typeFName << "()\n{\n" << indent << "static PyTypeObject *type = " << typeCreationFName << "();\nreturn type;\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); + ContainerCreationFlags flags; + if (kind == ContainerTypeEntry::SpanContainer) + flags.setFlag(ContainerCreationFlag::Allocate); + + writeContainerCreationFunc(s, u"create"_s + result.name, typeFName, + containerType.cppSignature(), flags); + flags.setFlag(ContainerCreationFlag::Const); + writeContainerCreationFunc(s, u"createConst"_s + result.name, typeFName, + containerType.cppSignature(), flags); // Check function - result.checkFunctionName = result.name + u"_Check"_qs; - s << "extern \"C\" int " << result.checkFunctionName << "(PyObject *" << pyArg - << ")\n{\n" << indent << "return " << pyArg << " != nullptr && " - << pyArg << " != Py_None && " << pyArg << "->ob_type == " + result.checkFunctionName = result.name + u"_Check"_s; + s << "extern \"C\" int " << result.checkFunctionName << "(PyObject *" << PYTHON_ARG + << ")\n{\n" << indent << "return " << PYTHON_ARG << " != nullptr && " + << PYTHON_ARG << " != Py_None && " << PYTHON_ARG << "->ob_type == " << typeFName << "();\n" << outdent << "}\n\n"; // SBK converter Python to C++ - result.pythonToConverterFunctionName = u"PythonToCpp"_qs + result.name; + result.pythonToConverterFunctionName = u"PythonToCpp"_s + result.name; s << "extern \"C\" void " << result.pythonToConverterFunctionName - << "(PyObject *" << pyArg << ", void *cppOut)\n{\n" << indent + << "(PyObject *" << PYTHON_ARG << ", void *cppOut)\n{\n" << indent << "auto *d = ShibokenSequenceContainerPrivate<" << cppSignature - << ">::get(" << pyArg << ");\n" + << ">::get(" << PYTHON_ARG << ");\n" << "*reinterpret_cast<" << cppSignature << "**>(cppOut) = d->m_list;\n" << outdent << "}\n\n"; // SBK check function for converting Python to C++ that returns the converter - result.converterCheckFunctionName = u"is"_qs + result.name + u"PythonToCppConvertible"_qs; + result.converterCheckFunctionName = u"is"_s + result.name + u"PythonToCppConvertible"_s; s << "extern \"C\" PythonToCppFunc " << result.converterCheckFunctionName - << "(PyObject *" << pyArg << ")\n{\n" << indent << "if (" - << result.checkFunctionName << '(' << pyArg << "))\n" << indent + << "(PyObject *" << PYTHON_ARG << ")\n{\n" << indent << "if (" + << result.checkFunctionName << '(' << PYTHON_ARG << "))\n" << indent << "return " << result.pythonToConverterFunctionName << ";\n" << outdent << "return {};\n" << outdent << "}\n\n"; |