aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp')
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp486
1 files changed, 486 insertions, 0 deletions
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp b/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp
new file mode 100644
index 000000000..1b893640a
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp
@@ -0,0 +1,486 @@
+// Copyright (C) 2023 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 "generatorcontext.h"
+#include <apiextractorresult.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <codesnip.h>
+#include <exception.h>
+#include <messages.h>
+#include <textstream.h>
+#include <overloaddata.h>
+#include <smartpointertypeentry.h>
+
+#include <QtCore/QDebug>
+
+using namespace Qt::StringLiterals;
+
+static const char smartPtrComment[] =
+ "// Try to find the 'name' attribute, by retrieving the PyObject for "
+ "the corresponding C++ object held by the smart pointer.\n";
+
+static QString smartPointerGetter(const GeneratorContext &context)
+{
+ const auto te = context.metaClass()->typeEntry();
+ Q_ASSERT(te->isSmartPointer());
+ return std::static_pointer_cast<const SmartPointerTypeEntry>(te)->getter();
+}
+
+struct callGetter
+{
+ explicit callGetter(const GeneratorContext &context) : m_context(context) {}
+
+ const GeneratorContext &m_context;
+};
+
+TextStream &operator<<(TextStream &str, const callGetter &c)
+{
+ str << "PyObject_CallMethod(self, \"" << smartPointerGetter(c.m_context) << "\", 0)";
+ return str;
+}
+
+// Helpers to collect all smart pointer pointee base classes
+static AbstractMetaClassCList
+ findSmartPointeeBaseClasses(const ApiExtractorResult &api,
+ const AbstractMetaType &smartPointerType)
+{
+ AbstractMetaClassCList result;
+ auto instantiationsTe = smartPointerType.instantiations().at(0).typeEntry();
+ auto targetClass = AbstractMetaClass::findClass(api.classes(), instantiationsTe);
+ if (targetClass != nullptr)
+ result = targetClass->allTypeSystemAncestors();
+ return result;
+}
+
+using ComparisonOperatorList = QList<AbstractMetaFunction::ComparisonOperatorType>;
+
+// Return the available comparison operators for smart pointers
+static ComparisonOperatorList smartPointeeComparisons(const GeneratorContext &context)
+{
+ Q_ASSERT(context.forSmartPointer());
+ auto te = context.preciseType().instantiations().constFirst().typeEntry();
+ if (isExtendedCppPrimitive(te)) { // Primitive pointee types have all
+ return {AbstractMetaFunction::OperatorEqual,
+ AbstractMetaFunction::OperatorNotEqual,
+ AbstractMetaFunction::OperatorLess,
+ AbstractMetaFunction::OperatorLessEqual,
+ AbstractMetaFunction::OperatorGreater,
+ AbstractMetaFunction::OperatorGreaterEqual};
+ }
+
+ const auto pointeeClass = context.pointeeClass();
+ if (!pointeeClass)
+ return {};
+
+ ComparisonOperatorList result;
+ const auto &comparisons =
+ pointeeClass->operatorOverloads(OperatorQueryOption::SymmetricalComparisonOp);
+ for (const auto &f : comparisons) {
+ const auto ct = f->comparisonOperatorType().value();
+ if (!result.contains(ct))
+ result.append(ct);
+ }
+ return result;
+}
+
+std::optional<AbstractMetaType>
+ CppGenerator::findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer,
+ const TypeEntryCPtr &pointee) const
+{
+ for (const auto &smp : api().instantiatedSmartPointers()) {
+ const auto &i = smp.type;
+ if (i.typeEntry() == pointer && i.instantiations().at(0).typeEntry() == pointee)
+ return i;
+ }
+ return {};
+}
+
+static bool hasParameterPredicate(const AbstractMetaFunctionCPtr &f)
+{
+ return !f->arguments().isEmpty();
+}
+
+void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext)
+{
+ s.setLanguage(TextStream::Language::Cpp);
+ AbstractMetaClassCPtr metaClass = classContext.metaClass();
+ const auto typeEntry = std::static_pointer_cast<const SmartPointerTypeEntry>(metaClass->typeEntry());
+ const bool hasPointeeClass = classContext.pointeeClass() != nullptr;
+ const auto smartPointerType = typeEntry->smartPointerType();
+ const bool isValueHandle = smartPointerType ==TypeSystem::SmartPointerType::ValueHandle;
+
+ IncludeGroup includes{u"Extra includes"_s, typeEntry->extraIncludes()};
+ if (hasPointeeClass)
+ includes.append(classContext.pointeeClass()->typeEntry()->include());
+ includes.includes.append({Include::IncludePath, u"sbksmartpointer.h"_s});
+ generateIncludes(s, classContext, {includes});
+
+ s << '\n';
+
+ // class inject-code native/beginning
+ if (!typeEntry->codeSnips().isEmpty()) {
+ writeClassCodeSnips(s, typeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode,
+ classContext);
+ s << '\n';
+ }
+
+ StringStream smd(TextStream::Language::Cpp);
+ StringStream md(TextStream::Language::Cpp);
+ StringStream signatureStream(TextStream::Language::Cpp);
+
+ s << openTargetExternC;
+
+ const auto &functionGroups = getFunctionGroups(metaClass);
+
+ // Skip all public methods of the smart pointer except for the special
+ // methods declared in the type entry.
+
+ auto ctors = metaClass->queryFunctions(FunctionQueryOption::Constructors);
+ if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*"
+ auto end = std::remove_if(ctors.begin(), ctors.end(), hasParameterPredicate);
+ ctors.erase(end, ctors.end());
+ }
+
+ if (!ctors.isEmpty()) {
+ OverloadData overloadData(ctors, api());
+ writeConstructorWrapper(s, overloadData, classContext);
+ writeSignatureInfo(signatureStream, overloadData);
+ }
+
+ if (!typeEntry->resetMethod().isEmpty()) {
+ auto it = functionGroups.constFind(typeEntry->resetMethod());
+ if (it == functionGroups.cend())
+ throw Exception(msgCannotFindSmartPointerMethod(typeEntry, typeEntry->resetMethod()));
+ AbstractMetaFunctionCList resets = it.value();
+ if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*"
+ auto end = std::remove_if(resets.begin(), resets.end(), hasParameterPredicate);
+ resets.erase(end, resets.end());
+ }
+ if (!resets.isEmpty())
+ writeMethodWrapper(s, md, signatureStream, resets, classContext);
+ }
+
+ auto it = functionGroups.constFind(typeEntry->getter());
+ if (it == functionGroups.cend() || it.value().size() != 1)
+ throw Exception(msgCannotFindSmartPointerGetter(typeEntry));
+
+ writeMethodWrapper(s, md, signatureStream, it.value(), classContext);
+
+ QStringList optionalMethods;
+ if (!typeEntry->refCountMethodName().isEmpty())
+ optionalMethods.append(typeEntry->refCountMethodName());
+ const QString valueCheckMethod = typeEntry->valueCheckMethod();
+ if (!valueCheckMethod.isEmpty() && !valueCheckMethod.startsWith(u"operator"))
+ optionalMethods.append(valueCheckMethod);
+ if (!typeEntry->nullCheckMethod().isEmpty())
+ optionalMethods.append(typeEntry->nullCheckMethod());
+
+ for (const QString &optionalMethod : optionalMethods) {
+ auto it = functionGroups.constFind(optionalMethod);
+ if (it == functionGroups.cend() || it.value().size() != 1)
+ throw Exception(msgCannotFindSmartPointerMethod(typeEntry, optionalMethod));
+ writeMethodWrapper(s, md, signatureStream, it.value(), classContext);
+ }
+
+ writeCopyFunction(s, md, signatureStream, classContext);
+ writeSmartPointerDirFunction(s, md, signatureStream, classContext);
+
+ const QString methodsDefinitions = md.toString();
+ const QString singleMethodDefinitions = smd.toString();
+
+ const QString className = chopType(cpythonTypeName(typeEntry));
+
+ // Write single method definitions
+ s << singleMethodDefinitions;
+
+ // Write methods definition
+ writePyMethodDefs(s, className, methodsDefinitions);
+
+ // Write tp_s/getattro function
+ const auto boolCastOpt = boolCast(metaClass);
+ writeSmartPointerGetattroFunction(s, classContext, boolCastOpt);
+ writeSmartPointerSetattroFunction(s, classContext);
+
+ if (boolCastOpt.has_value())
+ writeNbBoolFunction(classContext, boolCastOpt.value(), s);
+
+ if (smartPointerType == TypeSystem::SmartPointerType::Shared)
+ writeSmartPointerRichCompareFunction(s, classContext);
+
+ s << closeExternC;
+
+ if (hasHashFunction(metaClass))
+ writeHashFunction(s, classContext);
+
+ // Write tp_traverse and tp_clear functions.
+ writeTpTraverseFunction(s, metaClass);
+ writeTpClearFunction(s, metaClass);
+
+ writeClassDefinition(s, metaClass, classContext);
+
+ s << '\n';
+
+ writeConverterFunctions(s, metaClass, classContext);
+ writeClassRegister(s, metaClass, classContext, signatureStream);
+
+ // class inject-code native/end
+ if (!typeEntry->codeSnips().isEmpty()) {
+ writeClassCodeSnips(s, typeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode,
+ classContext);
+ s << '\n';
+ }
+}
+
+void CppGenerator::writeSmartPointerConverterFunctions(TextStream &s,
+ const AbstractMetaType &smartPointerType) const
+{
+ const auto baseClasses = findSmartPointeeBaseClasses(api(), smartPointerType);
+ if (baseClasses.isEmpty())
+ return;
+
+ auto smartPointerTypeEntry =
+ std::static_pointer_cast<const SmartPointerTypeEntry>(smartPointerType.typeEntry());
+
+ // TODO: Missing conversion to smart pointer pointer type:
+
+ s << "// Register smartpointer conversion for all derived classes\n";
+ for (const auto &base : baseClasses) {
+ auto baseTe = base->typeEntry();
+ if (smartPointerTypeEntry->matchesInstantiation(baseTe)) {
+ if (auto opt = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) {
+ const auto smartTargetType = opt.value();
+ s << "// SmartPointer derived class: "
+ << smartTargetType.cppSignature() << "\n";
+ writePythonToCppConversionFunctions(s, smartPointerType,
+ smartTargetType, {}, {}, {});
+ }
+ }
+ }
+}
+
+void CppGenerator::writeSmartPointerCppSelfConversion(TextStream &s,
+ const GeneratorContext &context)
+{
+ Q_ASSERT(context.forSmartPointer());
+ s << cpythonWrapperCPtr(context.preciseType(), u"self"_s);
+}
+
+void CppGenerator::writeSmartPointerCppSelfDefinition(TextStream &s,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn,
+ CppSelfDefinitionFlags flags)
+{
+ Q_ASSERT(context.forSmartPointer());
+ writeInvalidPyObjectCheck(s, u"self"_s, errorReturn);
+ writeCppSelfVarDef(s, flags);
+ writeSmartPointerCppSelfConversion(s, context);
+ s << ";\n";
+}
+
+void CppGenerator::writeSmartPointerConverterInitialization(TextStream &s,
+ const AbstractMetaType &type) const
+{
+ const QByteArray cppSignature = type.cppSignature().toUtf8();
+ auto writeConversionRegister = [&s](const AbstractMetaType &sourceType,
+ const QString &targetTypeName,
+ const QString &targetConverter)
+ {
+ const QString sourceTypeName = fixedCppTypeName(sourceType);
+ const QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName);
+ const QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName);
+
+ writeAddPythonToCppConversion(s, targetConverter, toCpp, isConv);
+ };
+
+ const auto classes = findSmartPointeeBaseClasses(api(), type);
+ if (classes.isEmpty())
+ return;
+
+ auto smartPointerTypeEntry = std::static_pointer_cast<const SmartPointerTypeEntry>(type.typeEntry());
+
+ s << "// Register SmartPointer converter for type '" << cppSignature << "'." << '\n'
+ << "///////////////////////////////////////////////////////////////////////////////////////\n\n";
+
+ for (const auto &base : classes) {
+ auto baseTe = base->typeEntry();
+ if (auto opt = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) {
+ const auto smartTargetType = opt.value();
+ s << "// Convert to SmartPointer derived class: ["
+ << smartTargetType.cppSignature() << "]\n";
+ const QString converter = u"Shiboken::Conversions::getConverter(\""_s
+ + smartTargetType.cppSignature() + u"\")"_s;
+ writeConversionRegister(type, fixedCppTypeName(smartTargetType), converter);
+ } else {
+ s << "// Class not found:" << type.instantiations().at(0).cppSignature();
+ }
+ }
+
+ s << "///////////////////////////////////////////////////////////////////////////////////////" << '\n' << '\n';
+}
+
+void CppGenerator::writeSmartPointerRichCompareFunction(TextStream &s,
+ const GeneratorContext &context) const
+{
+ static const char selfPointeeVar[] = "cppSelfPointee";
+ static const char cppArg0PointeeVar[] = "cppArg0Pointee";
+
+ const auto metaClass = context.metaClass();
+ QString baseName = cpythonBaseName(metaClass);
+ writeRichCompareFunctionHeader(s, baseName, context);
+
+ s << "if (";
+ writeTypeCheck(s, context.preciseType(), PYTHON_ARG);
+ s << ") {\n" << indent;
+ writeArgumentConversion(s, context.preciseType(), CPP_ARG0,
+ PYTHON_ARG, ErrorReturn::Default, metaClass);
+
+ const auto te = context.preciseType().typeEntry();
+ Q_ASSERT(te->isSmartPointer());
+ const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(te);
+
+ s << "const auto *" << selfPointeeVar << " = " << CPP_SELF_VAR
+ << '.' << ste->getter() << "();\n";
+ s << "const auto *" << cppArg0PointeeVar << " = " << CPP_ARG0
+ << '.' << ste->getter() << "();\n";
+
+ // If we have an object without any comparisons, only generate a simple
+ // equality check by pointee address
+ auto availableOps = smartPointeeComparisons(context);
+ const bool comparePointeeAddressOnly = availableOps.isEmpty();
+ if (comparePointeeAddressOnly) {
+ availableOps << AbstractMetaFunction::OperatorEqual
+ << AbstractMetaFunction::OperatorNotEqual;
+ } else {
+ // For value types with operators, we complain about nullptr
+ s << "if (" << selfPointeeVar << " == nullptr || " << cppArg0PointeeVar
+ << " == nullptr) {\n" << indent
+ << "PyErr_SetString(PyExc_NotImplementedError, \"nullptr passed to comparison.\");\n"
+ << ErrorReturn::Default << '\n' << outdent << "}\n";
+ }
+
+ s << "bool " << CPP_RETURN_VAR << "= false;\n"
+ << "switch (op) {\n";
+ for (auto op : availableOps) {
+ s << "case " << AbstractMetaFunction::pythonRichCompareOpCode(op) << ":\n"
+ << indent << CPP_RETURN_VAR << " = ";
+ if (comparePointeeAddressOnly) {
+ s << selfPointeeVar << ' ' << AbstractMetaFunction::cppComparisonOperator(op)
+ << ' ' << cppArg0PointeeVar << ";\n";
+ } else {
+ // Shortcut for equality: Check pointee address
+ if (op == AbstractMetaFunction::OperatorEqual
+ || op == AbstractMetaFunction::OperatorLessEqual
+ || op == AbstractMetaFunction::OperatorGreaterEqual) {
+ s << selfPointeeVar << " == " << cppArg0PointeeVar << " || ";
+ }
+ // Generate object's comparison
+ s << "*" << selfPointeeVar << ' '
+ << AbstractMetaFunction::cppComparisonOperator(op) << " *"
+ << cppArg0PointeeVar << ";\n";
+ }
+ s << "break;\n" << outdent;
+
+ }
+ if (availableOps.size() < 6) {
+ s << "default:\n" << indent
+ << richCompareComment
+ << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" << outdent;
+ }
+ s << "}\n" << PYTHON_RETURN_VAR << " = " << CPP_RETURN_VAR
+ << " ? Py_True : Py_False;\n"
+ << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n"
+ << outdent << "}\n"
+ << "return Shiboken::returnFromRichCompare(" << PYTHON_RETURN_VAR << ");\n"
+ << outdent << "}\n\n";
+}
+
+void CppGenerator::writeSmartPointerSetattroFunction(TextStream &s,
+ const GeneratorContext &context)
+{
+ Q_ASSERT(context.forSmartPointer());
+ writeSetattroDefinition(s, context.metaClass());
+ s << smartPtrComment
+ << "if (auto *rawObj = " << callGetter(context) << ") {\n" << indent
+ << "if (PyObject_HasAttr(rawObj, name) != 0)\n" << indent
+ << "return PyObject_GenericSetAttr(rawObj, name, value);\n" << outdent
+ << "Py_DECREF(rawObj);\n" << outdent
+ << "}\n";
+ writeSetattroDefaultReturn(s);
+}
+
+void CppGenerator::writeSmartPointerGetattroFunction(TextStream &s,
+ const GeneratorContext &context,
+ const BoolCastFunctionOptional &boolCast)
+{
+ Q_ASSERT(context.forSmartPointer());
+ const auto metaClass = context.metaClass();
+ writeGetattroDefinition(s, metaClass);
+ s << "PyObject *tmp = PyObject_GenericGetAttr(self, name);\n"
+ << "if (tmp)\n" << indent << "return tmp;\n" << outdent
+ << "if (PyErr_ExceptionMatches(PyExc_AttributeError) == 0)\n"
+ << indent << "return nullptr;\n" << outdent
+ << "PyErr_Clear();\n";
+
+ if (boolCast.has_value()) {
+ writeSmartPointerCppSelfDefinition(s, context);
+ s << "if (";
+ writeNbBoolExpression(s, boolCast.value(), true /* invert */);
+ s << ") {\n" << indent
+ << R"(PyTypeObject *tp = Py_TYPE(self);
+PyErr_Format(PyExc_AttributeError, "Attempt to retrieve '%s' from null object '%s'.",
+ Shiboken::String::toCString(name), tp->tp_name);
+return nullptr;
+)" << outdent << "}\n";
+ }
+
+ // This generates the code which dispatches access to member functions
+ // and fields from the smart pointer to its pointee.
+ s << smartPtrComment
+ << "if (auto *rawObj = " << callGetter(context) << ") {\n" << indent
+ << "if (auto *attribute = PyObject_GetAttr(rawObj, name))\n"
+ << indent << "tmp = attribute;\n" << outdent
+ << "Py_DECREF(rawObj);\n" << outdent
+ << "}\n"
+ << "if (!tmp) {\n" << indent
+ << R"(PyTypeObject *tp = Py_TYPE(self);
+PyErr_Format(PyExc_AttributeError,
+ "'%.50s' object has no attribute '%.400s'",
+ tp->tp_name, Shiboken::String::toCString(name));
+)" << outdent
+ << "}\n"
+ << "return tmp;\n" << outdent << "}\n\n";
+}
+
+QString CppGenerator::writeSmartPointerReprFunction(TextStream &s,
+ const GeneratorContext &context)
+{
+ const auto metaClass = context.metaClass();
+ QString funcName = writeReprFunctionHeader(s, context);
+ s << "Shiboken::AutoDecRef pointee(" << callGetter(context) << ");\n"
+ << "return Shiboken::SmartPointer::repr(self, pointee);\n";
+ writeReprFunctionFooter(s);
+ return funcName;
+}
+
+QString CppGenerator::writeSmartPointerDirFunction(TextStream &s, TextStream &definitionStream,
+ TextStream &signatureStream,
+ const GeneratorContext &context)
+{
+ QString funcName = cpythonBaseName(context.metaClass()) + u"__dir__"_s;
+
+ signatureStream << fullPythonClassName(context.metaClass()) << ".__dir__()\n";
+ definitionStream << PyMethodDefEntry{u"__dir__"_s, funcName, {"METH_NOARGS"_ba}, {}}
+ << ",\n";
+
+ s << "extern \"C\"\n{\n"
+ << "static PyObject *" << funcName << "(PyObject *self)\n{\n" << indent
+ << "Shiboken::AutoDecRef pointee(" << callGetter(context) << ");\n"
+ << "return Shiboken::SmartPointer::dir(self, pointee);\n"
+ << outdent << "}\n} // extern C\n\n";
+ return funcName;
+}