aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/generator/shiboken/headergenerator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/generator/shiboken/headergenerator.cpp')
-rw-r--r--sources/shiboken6/generator/shiboken/headergenerator.cpp817
1 files changed, 552 insertions, 265 deletions
diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp
index 21d5c8f6e..1f574b47c 100644
--- a/sources/shiboken6/generator/shiboken/headergenerator.cpp
+++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp
@@ -2,14 +2,18 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "headergenerator.h"
+#include "configurablescope.h"
+#include "generatorcontext.h"
#include <apiextractorresult.h>
+#include <abstractmetaargument.h>
#include <abstractmetaenum.h>
#include <abstractmetafield.h>
#include <abstractmetafunction.h>
#include <abstractmetalang.h>
#include <abstractmetalang_helpers.h>
#include <codesnip.h>
-#include <modifications.h>
+#include <clangparser/compilersupport.h>
+#include <exception.h>
#include <typedatabase.h>
#include <reporthandler.h>
#include <textstream.h>
@@ -17,16 +21,16 @@
#include "containertypeentry.h"
#include "enumtypeentry.h"
#include "flagstypeentry.h"
+#include <messages.h>
#include "namespacetypeentry.h"
#include "primitivetypeentry.h"
#include "typedefentry.h"
#include "typesystemtypeentry.h"
-#include "parser/codemodel.h"
-
#include "qtcompat.h"
#include <algorithm>
+#include <set>
#include <QtCore/QDir>
#include <QtCore/QTextStream>
@@ -35,23 +39,68 @@
using namespace Qt::StringLiterals;
-QString HeaderGenerator::headerFileNameForContext(const GeneratorContext &context)
+struct IndexValue
+{
+ QString name; // "SBK_..."
+ int value;
+ QString comment;
+};
+
+TextStream &operator<<(TextStream &s, const IndexValue &iv)
{
- return fileNameForContextHelper(context, u"_wrapper.h"_s);
+ s << " " << AlignedField(iv.name, 56) << " = " << iv.value << ',';
+ if (!iv.comment.isEmpty())
+ s << " // " << iv.comment;
+ s << '\n';
+ return s;
}
+// PYSIDE-504: Handling the "protected hack"
+// The problem: Creating wrappers when the class has private destructors.
+// You can see an example on Windows in qclipboard_wrapper.h and others.
+// Simply search for the text "// C++11: need to declare (unimplemented) destructor".
+// The protected hack is the definition "#define protected public".
+// For most compilers, this "hack" is enabled, because the problem of private
+// destructors simply vanishes.
+//
+// If one does not want to use this hack, then a new problem arises:
+// C++11 requires that a destructor is declared in a wrapper class when it is
+// private in the base class. There is no implementation allowed!
+//
+// Unfortunately, MSVC in recent versions supports C++11, and due to restrictive
+// rules, it is impossible to use the hack with this compiler.
+// More unfortunate: Clang, when C++11 is enabled, also enforces a declaration
+// of a private destructor, but it falsely then creates a linker error!
+//
+// Originally, we wanted to remove the protected hack. But due to the Clang
+// problem, we gave up on removal of the protected hack and use it always
+// when we can. This might change again when the Clang problem is solved.
+
+static bool alwaysGenerateDestructorDeclaration()
+{
+ return clang::compiler() == Compiler::Msvc;
+}
+
+const char *HeaderGenerator::protectedHackDefine = R"(// Workaround to access protected functions
+#ifndef protected
+# define protected public
+#endif
+
+)";
+
QString HeaderGenerator::fileNameForContext(const GeneratorContext &context) const
{
return headerFileNameForContext(context);
}
-void HeaderGenerator::writeCopyCtor(TextStream &s, const AbstractMetaClass *metaClass) const
+void HeaderGenerator::writeCopyCtor(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass)
{
s << wrapperName(metaClass) << "(const " << metaClass->qualifiedCppName()
<< "& self) : " << metaClass->qualifiedCppName() << "(self)\n{\n}\n\n";
}
-static void writeProtectedEnums(TextStream &s, const AbstractMetaClass *metaClass)
+static void writeProtectedEnums(TextStream &s, const AbstractMetaClassCPtr &metaClass)
{
const QString name = metaClass->qualifiedCppName();
for (const auto &e : metaClass->enums()) {
@@ -60,132 +109,181 @@ static void writeProtectedEnums(TextStream &s, const AbstractMetaClass *metaClas
}
}
-void HeaderGenerator::generateClass(TextStream &s, const GeneratorContext &classContextIn)
+void HeaderGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
{
- GeneratorContext classContext = classContextIn;
- const AbstractMetaClass *metaClass = classContext.metaClass();
- m_inheritedOverloads.clear();
+ const AbstractMetaClassCPtr metaClass = classContext.metaClass();
// write license comment
s << licenseComment();
QString wrapperName = classContext.effectiveClassName();
- QString outerHeaderGuard = getFilteredCppSignatureString(wrapperName).toUpper();
- QString innerHeaderGuard;
+ QString outerHeaderGuard = getFilteredCppSignatureString(wrapperName);
// Header
s << "#ifndef SBK_" << outerHeaderGuard << "_H\n";
s << "#define SBK_" << outerHeaderGuard << "_H\n\n";
if (!avoidProtectedHack())
- s << "#define protected public\n\n";
+ s << protectedHackDefine;
- //Includes
- auto typeEntry = metaClass->typeEntry();
- s << typeEntry->include() << '\n';
- if (classContext.useWrapper() && !typeEntry->extraIncludes().isEmpty()) {
- s << "\n// Extra includes\n";
- for (const Include &inc : typeEntry->extraIncludes())
- s << inc.toString() << '\n';
+ // Includes
+ s << metaClass->typeEntry()->include() << '\n';
+ for (auto &inst : metaClass->templateBaseClassInstantiations())
+ s << inst.typeEntry()->include();
+
+ if (classContext.useWrapper())
+ writeWrapperClass(s, wrapperName, classContext);
+
+ s << "#endif // SBK_" << outerHeaderGuard << "_H\n\n";
+}
+
+void HeaderGenerator::writeWrapperClass(TextStream &s,
+ const QString &wrapperName,
+ const GeneratorContext &classContext) const
+{
+ const auto metaClass = classContext.metaClass();
+
+ if (avoidProtectedHack()) {
+ const auto includeGroups = classIncludes(metaClass);
+ for( const auto &includeGroup : includeGroups)
+ s << includeGroup;
}
- if (classContext.useWrapper() && usePySideExtensions() && metaClass->isQObject())
+ if (usePySideExtensions() && isQObject(metaClass))
s << "namespace PySide { class DynamicQMetaObject; }\n\n";
- while (classContext.useWrapper()) {
- if (!innerHeaderGuard.isEmpty()) {
- s << "# ifndef SBK_" << innerHeaderGuard << "_H\n";
- s << "# define SBK_" << innerHeaderGuard << "_H\n\n";
- s << "// Inherited base class:\n";
+ writeWrapperClassDeclaration(s, wrapperName, classContext);
+
+ // PYSIDE-500: Use also includes for inherited wrapper classes other
+ // modules, because without the protected hack, we sometimes need to
+ // cast inherited wrappers. CppGenerator generates include statements for
+ // the classes of the current module. For other modules, we insert the
+ // declarations as recursive headers, since wrapper headers are not
+ // installed. This keeps the file structure as simple as before the
+ // enhanced inheritance.
+ if (avoidProtectedHack()) {
+ const auto &baseClasses = allBaseClasses(classContext.metaClass());
+ for (const auto &baseClass : baseClasses) {
+ const auto gen = baseClass->typeEntry()->codeGeneration();
+ if (gen == TypeEntry::GenerateForSubclass) { // other module
+ const auto baseContext = contextForClass(baseClass);
+ if (baseContext.useWrapper())
+ writeInheritedWrapperClassDeclaration(s, baseContext);
+ }
}
+ }
+}
- // Class
- s << "class " << wrapperName
- << " : public " << metaClass->qualifiedCppName()
- << "\n{\npublic:\n" << indent;
-
- // Make protected enums accessible
- if (avoidProtectedHack()) {
- recurseClassHierarchy(metaClass, [&s] (const AbstractMetaClass *metaClass) {
- writeProtectedEnums(s, metaClass);
- return false;
- });
- }
+void HeaderGenerator::writeInheritedWrapperClassDeclaration(TextStream &s,
+ const GeneratorContext &classContext) const
+{
+ const QString wrapperName = classContext.effectiveClassName();
+ const QString innerHeaderGuard =
+ getFilteredCppSignatureString(wrapperName).toUpper();
- if (avoidProtectedHack() && metaClass->hasProtectedFields()) {
- s << "\n// Make protected fields accessible\n";
- const QString name = metaClass->qualifiedCppName();
- for (const auto &f : metaClass->fields()) {
- if (f.isProtected())
- s << "using " << name << "::" << f.originalName() << ";\n";
- }
- s << '\n';
- }
+ s << "# ifndef SBK_" << innerHeaderGuard << "_H\n"
+ << "# define SBK_" << innerHeaderGuard << "_H\n\n"
+ << "// Inherited base class:\n";
- int maxOverrides = 0;
- for (const auto &func : metaClass->functions()) {
- const auto generation = functionGeneration(func);
- writeFunction(s, func, generation);
- // PYSIDE-803: Build a boolean cache for unused overrides.
- if (generation.testFlag(FunctionGenerationFlag::VirtualMethod))
- maxOverrides++;
- }
- if (!maxOverrides)
- maxOverrides = 1;
-
- //destructor
- // PYSIDE-504: When C++ 11 is used, then the destructor must always be declared.
- // See abstractmetalang.cpp, determineCppWrapper() and generator.h for further
- // reference.
- if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor() || alwaysGenerateDestructor) {
- if (avoidProtectedHack() && metaClass->hasPrivateDestructor())
- s << "// C++11: need to declare (unimplemented) destructor because "
- "the base class destructor is private.\n";
- s << '~' << wrapperName << "();\n";
+ writeWrapperClassDeclaration(s, wrapperName, classContext);
+
+ s << "# endif // SBK_" << innerHeaderGuard << "_H\n\n";
+}
+
+void HeaderGenerator::writeWrapperClassDeclaration(TextStream &s,
+ const QString &wrapperName,
+ const GeneratorContext &classContext) const
+{
+ const AbstractMetaClassCPtr metaClass = classContext.metaClass();
+ const auto typeEntry = metaClass->typeEntry();
+ InheritedOverloadSet inheritedOverloads;
+
+ // write license comment
+ s << licenseComment();
+
+ // Class
+ s << "class " << wrapperName
+ << " : public " << metaClass->qualifiedCppName()
+ << "\n{\npublic:\n" << indent
+ << wrapperName << "(const " << wrapperName << " &) = delete;\n"
+ << wrapperName << "& operator=(const " << wrapperName << " &) = delete;\n"
+ << wrapperName << '(' << wrapperName << " &&) = delete;\n"
+ << wrapperName << "& operator=(" << wrapperName << " &&) = delete;\n\n";
+
+ // Make protected enums accessible
+ if (avoidProtectedHack()) {
+ recurseClassHierarchy(metaClass, [&s] (const AbstractMetaClassCPtr &metaClass) {
+ writeProtectedEnums(s, metaClass);
+ return false;
+ });
+ }
+
+ if (avoidProtectedHack() && metaClass->hasProtectedFields()) {
+ s << "\n// Make protected fields accessible\n";
+ const QString name = metaClass->qualifiedCppName();
+ for (const auto &f : metaClass->fields()) {
+ if (f.isProtected())
+ s << "using " << name << "::" << f.originalName() << ";\n";
}
+ s << '\n';
+ }
- writeClassCodeSnips(s, typeEntry->codeSnips(),
- TypeSystem::CodeSnipPositionDeclaration, TypeSystem::NativeCode,
- classContext);
+ int maxOverrides = 0;
+ for (const auto &func : metaClass->functions()) {
+ const auto generation = functionGeneration(func);
+ writeFunction(s, func, &inheritedOverloads, generation);
+ // PYSIDE-803: Build a boolean cache for unused overrides.
+ if (generation.testFlag(FunctionGenerationFlag::VirtualMethod))
+ maxOverrides++;
+ }
+ if (!maxOverrides)
+ maxOverrides = 1;
+
+ //destructor
+ // PYSIDE-504: When C++ 11 is used, then the destructor must always be declared.
+ if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()
+ || alwaysGenerateDestructorDeclaration()) {
+ if (avoidProtectedHack() && metaClass->hasPrivateDestructor())
+ s << "// C++11: need to declare (unimplemented) destructor because "
+ "the base class destructor is private.\n";
+ s << '~' << wrapperName << "()";
+ if (metaClass->hasVirtualDestructor())
+ s << " override";
+ s << ";\n";
+ }
+
+ writeClassCodeSnips(s, typeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionDeclaration, TypeSystem::NativeCode,
+ classContext);
- if ((!avoidProtectedHack() || !metaClass->hasPrivateDestructor())
- && usePySideExtensions() && metaClass->isQObject()) {
- s << outdent << "public:\n" << indent <<
-R"(int qt_metacall(QMetaObject::Call call, int id, void **args) override;
+ if (shouldGenerateMetaObjectFunctions(metaClass)) {
+ s << R"(
+const ::QMetaObject * metaObject() const override;
+int qt_metacall(QMetaObject::Call call, int id, void **args) override;
void *qt_metacast(const char *_clname) override;
)";
- }
+ }
- if (!m_inheritedOverloads.isEmpty()) {
- s << "// Inherited overloads, because the using keyword sux\n";
- for (const auto &func : qAsConst(m_inheritedOverloads))
- writeMemberFunctionWrapper(s, func);
- m_inheritedOverloads.clear();
- }
+ if (!inheritedOverloads.isEmpty()) {
+ s << "// Inherited overloads, because the using keyword sux\n";
+ for (const auto &func : std::as_const(inheritedOverloads))
+ writeMemberFunctionWrapper(s, func);
+ }
- if (usePySideExtensions())
- s << "static void pysideInitQtMetaTypes();\n";
+ if (usePySideExtensions())
+ s << "static void pysideInitQtMetaTypes();\n";
- s << "void resetPyMethodCache();\n"
- << outdent << "private:\n" << indent
- << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n"
- << outdent << "};\n\n";
- if (!innerHeaderGuard.isEmpty())
- s << "# endif // SBK_" << innerHeaderGuard << "_H\n\n";
+ s << "void resetPyMethodCache();\n"
+ << outdent << "private:\n" << indent;
- // PYSIDE-500: Use also includes for inherited wrapper classes, because
- // without the protected hack, we sometimes need to cast inherited wrappers.
- // But we don't use multiple include files. Instead, they are inserted as recursive
- // headers. This keeps the file structure as simple as before the enhanced inheritance.
- metaClass = metaClass->baseClass();
- if (!metaClass || !avoidProtectedHack())
- break;
- classContext = contextForClass(metaClass);
- wrapperName = classContext.effectiveClassName();
- innerHeaderGuard = getFilteredCppSignatureString(wrapperName).toUpper();
+ if (!metaClass->userAddedPythonOverrides().isEmpty()) {
+ for (const auto &f : metaClass->userAddedPythonOverrides())
+ s << functionSignature(f, {}, {}, Generator::OriginalTypeDescription) << ";\n";
+ s << '\n';
}
- s << "#endif // SBK_" << outerHeaderGuard << "_H\n\n";
+ s << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n"
+ << outdent << "};\n\n";
}
// Write an inline wrapper around a function
@@ -195,8 +293,6 @@ void HeaderGenerator::writeMemberFunctionWrapper(TextStream &s,
{
Q_ASSERT(!func->isConstructor() && !func->isOperatorOverload());
s << "inline ";
- if (func->isStatic())
- s << "static ";
s << functionSignature(func, {}, postfix, Generator::OriginalTypeDescription)
<< " { ";
if (!func->isVoid())
@@ -217,9 +313,9 @@ void HeaderGenerator::writeMemberFunctionWrapper(TextStream &s,
s << ", ";
const AbstractMetaArgument &arg = arguments.at(i);
const auto &type = arg.type();
- const TypeEntry *enumTypeEntry = nullptr;
+ TypeEntryCPtr enumTypeEntry;
if (type.isFlags())
- enumTypeEntry = static_cast<const FlagsTypeEntry *>(type.typeEntry())->originator();
+ enumTypeEntry = std::static_pointer_cast<const FlagsTypeEntry>(type.typeEntry())->originator();
else if (type.isEnum())
enumTypeEntry = type.typeEntry();
if (enumTypeEntry) {
@@ -234,7 +330,8 @@ void HeaderGenerator::writeMemberFunctionWrapper(TextStream &s,
}
void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
- FunctionGeneration generation)
+ InheritedOverloadSet *inheritedOverloads,
+ FunctionGeneration generation) const
{
// do not write copy ctors here.
@@ -254,7 +351,7 @@ void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPt
}
const bool isVirtual = generation.testFlag(FunctionGenerationFlag::VirtualMethod);
- if (isVirtual || generation.testFlag(FunctionGenerationFlag::QMetaObjectMethod)) {
+ if (isVirtual) {
s << functionSignature(func, {}, {}, Generator::OriginalTypeDescription)
<< " override;\n";
}
@@ -269,7 +366,7 @@ void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPt
&& !f->isAbstract()
&& !f->isStatic()
&& f->name() == func->name()) {
- m_inheritedOverloads << f;
+ inheritedOverloads->insert(f);
}
}
@@ -279,28 +376,14 @@ void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPt
}
}
-static void _writeTypeIndexValue(TextStream &s, const QString &variableName,
- int typeIndex)
-{
- s << " " << AlignedField(variableName, 56) << " = " << typeIndex;
-}
-
-static inline void _writeTypeIndexValueLine(TextStream &s,
- const QString &variableName,
- int typeIndex)
-{
- _writeTypeIndexValue(s, variableName, typeIndex);
- s << ",\n";
-}
-
// Find equivalent typedefs "using Foo=QList<int>", "using Bar=QList<int>"
-static const AbstractMetaClass *
+static AbstractMetaClassCPtr
findEquivalentTemplateTypedef(const AbstractMetaClassCList &haystack,
- const AbstractMetaClass *needle)
+ const AbstractMetaClassCPtr &needle)
{
- auto *templateBaseClass = needle->templateBaseClass();
+ auto templateBaseClass = needle->templateBaseClass();
const auto &instantiations = needle->templateBaseClassInstantiations();
- for (auto *candidate : haystack) {
+ for (const auto &candidate : haystack) {
if (candidate->isTypeDef()
&& candidate->templateBaseClass() == templateBaseClass
&& candidate->templateBaseClassInstantiations() == instantiations) {
@@ -310,19 +393,20 @@ static const AbstractMetaClass *
return nullptr;
}
-void HeaderGenerator::writeTypeIndexValueLine(TextStream &s, const ApiExtractorResult &api,
- const TypeEntry *typeEntry)
+void HeaderGenerator::collectTypeEntryTypeIndexes(const ApiExtractorResult &api,
+ const TypeEntryCPtr &typeEntry,
+ IndexValues *indexValues)
{
if (!typeEntry || !typeEntry->generateCode())
return;
- s.setFieldAlignment(QTextStream::AlignLeft);
const int typeIndex = typeEntry->sbkIndex();
- _writeTypeIndexValueLine(s, getTypeIndexVariableName(typeEntry), typeIndex);
+ indexValues->append({getTypeIndexVariableName(typeEntry), typeIndex, {}});
+
if (typeEntry->isComplex()) {
// For a typedef "using Foo=QList<int>", write a type index
// SBK_QLIST_INT besides SBK_FOO which is then matched by function
// argument. Check against duplicate typedefs for the same types.
- const auto *cType = static_cast<const ComplexTypeEntry *>(typeEntry);
+ const auto cType = std::static_pointer_cast<const ComplexTypeEntry>(typeEntry);
if (cType->baseContainerType()) {
auto metaClass = AbstractMetaClass::findClass(api.classes(), cType);
Q_ASSERT(metaClass != nullptr);
@@ -332,20 +416,21 @@ void HeaderGenerator::writeTypeIndexValueLine(TextStream &s, const ApiExtractorR
metaClass) == nullptr) {
const QString indexVariable =
getTypeAlternateTemplateIndexVariableName(metaClass);
- _writeTypeIndexValueLine(s, indexVariable, typeIndex);
+ indexValues->append({indexVariable, typeIndex, {}});
m_alternateTemplateIndexes.append(m_alternateTemplateIndexes);
}
}
}
if (typeEntry->isEnum()) {
- auto ete = static_cast<const EnumTypeEntry *>(typeEntry);
+ auto ete = std::static_pointer_cast<const EnumTypeEntry>(typeEntry);
if (ete->flags())
- writeTypeIndexValueLine(s, api, ete->flags());
+ collectTypeEntryTypeIndexes(api, ete->flags(), indexValues);
}
}
-void HeaderGenerator::writeTypeIndexValueLines(TextStream &s, const ApiExtractorResult &api,
- const AbstractMetaClass *metaClass)
+void HeaderGenerator::collectClassTypeIndexes(const ApiExtractorResult &api,
+ const AbstractMetaClassCPtr &metaClass,
+ IndexValues *indexValues)
{
auto typeEntry = metaClass->typeEntry();
if (!typeEntry->generateCode())
@@ -353,16 +438,16 @@ void HeaderGenerator::writeTypeIndexValueLines(TextStream &s, const ApiExtractor
// enum indices are required for invisible namespaces as well.
for (const AbstractMetaEnum &metaEnum : metaClass->enums()) {
if (!metaEnum.isPrivate())
- writeTypeIndexValueLine(s, api, metaEnum.typeEntry());
+ collectTypeEntryTypeIndexes(api, metaEnum.typeEntry(), indexValues);
}
if (NamespaceTypeEntry::isVisibleScope(typeEntry))
- writeTypeIndexValueLine(s, api, typeEntry);
+ collectTypeEntryTypeIndexes(api, typeEntry, indexValues);
}
// Format the typedefs for the typedef entries to be generated
static void formatTypeDefEntries(TextStream &s)
{
- QList<const TypedefEntry *> entries;
+ QList<TypedefEntryCPtr> entries;
const auto typeDbEntries = TypeDatabase::instance()->typedefEntries();
for (auto it = typeDbEntries.cbegin(), end = typeDbEntries.cend(); it != end; ++it) {
if (it.value()->generateCode() != 0)
@@ -371,76 +456,258 @@ static void formatTypeDefEntries(TextStream &s)
if (entries.isEmpty())
return;
s << "\n// typedef entries\n";
- for (const auto e : entries) {
+ for (const auto &e : entries) {
const QString name = e->qualifiedCppName();
// Fixme: simplify by using nested namespaces in C++ 17.
const auto components = QStringView{name}.split(u"::");
- const int nameSpaceCount = components.size() - 1;
- for (int n = 0; n < nameSpaceCount; ++n)
+ const auto nameSpaceCount = components.size() - 1;
+ for (qsizetype n = 0; n < nameSpaceCount; ++n)
s << "namespace " << components.at(n) << " {\n";
s << "using " << components.constLast() << " = " << e->sourceType() << ";\n";
- for (int n = 0; n < nameSpaceCount; ++n)
+ for (qsizetype n = 0; n < nameSpaceCount; ++n)
s << "}\n";
}
s << '\n';
}
+// Helpers for forward-declaring classes in the module header for the
+// specialization of the SbkType template functions. This is possible if the
+// class does not have inner types or enums which need to be known.
+static bool canForwardDeclare(const AbstractMetaClassCPtr &c)
+{
+ if (c->isNamespace() || !c->enums().isEmpty()
+ || !c->innerClasses().isEmpty() || c->isTypeDef()) {
+ return false;
+ }
+ if (auto encl = c->enclosingClass())
+ return encl->isNamespace();
+ return true;
+}
-bool HeaderGenerator::finishGeneration()
+static void writeForwardDeclaration(TextStream &s, const AbstractMetaClassCPtr &c)
{
- // Generate the main header for this module.
- // This header should be included by binding modules
- // extendind on top of this one.
- QSet<Include> includes;
- QSet<Include> privateIncludes;
- StringStream macrosStream(TextStream::Language::Cpp);
+ Q_ASSERT(!c->isNamespace());
+ const bool isStruct = c->attributes().testFlag(AbstractMetaClass::Struct);
+ s << (isStruct ? "struct " : "class ");
+ // Do not use name as this can be modified/renamed for target lang.
+ const QString qualifiedCppName = c->qualifiedCppName();
+ const auto lastQualifier = qualifiedCppName.lastIndexOf(u':');
+ if (lastQualifier != -1)
+ s << QStringView{qualifiedCppName}.mid(lastQualifier + 1);
+ else
+ s << qualifiedCppName;
+ s << ";\n";
+}
- const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips();
- if (!snips.isEmpty()) {
- writeCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration,
- TypeSystem::TargetLangCode);
+// Helpers for writing out namespaces hierarchically when writing class
+// forward declarations to the module header. Ensure inline namespaces
+// are marked as such (else clang complains) and namespaces are ordered.
+struct NameSpace {
+ AbstractMetaClassCPtr nameSpace;
+ AbstractMetaClassCList classes;
+};
+
+static bool operator<(const NameSpace &n1, const NameSpace &n2)
+{
+ return n1.nameSpace->name() < n2.nameSpace->name();
+}
+
+using NameSpaces = QList<NameSpace>;
+
+static qsizetype indexOf(const NameSpaces &nsps, const AbstractMetaClassCPtr &needle)
+{
+ for (qsizetype i = 0, count = nsps.size(); i < count; ++i) {
+ if (nsps.at(i).nameSpace == needle)
+ return i;
}
+ return -1;
+}
- macrosStream << "// Type indices\nenum : int {\n";
- auto classList = api().classes();
+static void writeNamespaceForwardDeclarationRecursion(TextStream &s, qsizetype idx,
+ const NameSpaces &nameSpaces)
+{
+ auto &root = nameSpaces.at(idx);
+ s << '\n';
+ if (root.nameSpace->isInlineNamespace())
+ s << "inline ";
+ s << "namespace " << root.nameSpace->name() << " {\n" << indent;
+ for (const auto &c : root.classes)
+ writeForwardDeclaration(s, c);
+
+ for (qsizetype i = 0, count = nameSpaces.size(); i < count; ++i) {
+ if (i != idx && nameSpaces.at(i).nameSpace->enclosingClass() == root.nameSpace)
+ writeNamespaceForwardDeclarationRecursion(s, i, nameSpaces);
+ }
+ s << outdent << "}\n";
+}
- std::sort(classList.begin(), classList.end(),
- [](const AbstractMetaClass *a, const AbstractMetaClass *b) {
- return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex();
- });
+static void writeForwardDeclarations(TextStream &s,
+ const AbstractMetaClassCList &classList)
+{
+ NameSpaces nameSpaces;
- for (const AbstractMetaClass *metaClass : classList)
- writeTypeIndexValueLines(macrosStream, api(), metaClass);
+ s << '\n';
+ auto typeSystemEntry = TypeDatabase::instance()->defaultTypeSystemType();
+ if (!typeSystemEntry->namespaceBegin().isEmpty())
+ s << typeSystemEntry->namespaceBegin() << '\n';
+
+ for (const auto &c : classList) {
+ if (auto encl = c->enclosingClass()) {
+ Q_ASSERT(encl->isNamespace());
+ auto idx = indexOf(nameSpaces, encl);
+ if (idx != -1) {
+ nameSpaces[idx].classes.append(c);
+ } else {
+ nameSpaces.append(NameSpace{encl, {c}});
+ for (auto enclNsp = encl->enclosingClass(); enclNsp;
+ enclNsp = enclNsp->enclosingClass()) {
+ idx = indexOf(nameSpaces, enclNsp);
+ if (idx == -1)
+ nameSpaces.append(NameSpace{enclNsp, {}});
+ }
+ }
+ } else {
+ writeForwardDeclaration(s, c);
+ }
+ }
+
+ std::sort(nameSpaces.begin(), nameSpaces.end());
+
+ // Recursively write out namespaces starting at the root elements.
+ for (qsizetype i = 0, count = nameSpaces.size(); i < count; ++i) {
+ const auto &nsp = nameSpaces.at(i);
+ if (nsp.nameSpace->enclosingClass() == nullptr)
+ writeNamespaceForwardDeclarationRecursion(s, i, nameSpaces);
+ }
+
+ if (!typeSystemEntry->namespaceEnd().isEmpty())
+ s << typeSystemEntry->namespaceEnd() << '\n';
+}
+
+// Include parameters required for the module/private module header
+
+using ConditionalIncludeMap = QMap<QString, IncludeGroup>;
+
+static TextStream &operator<<(TextStream &s, const ConditionalIncludeMap &m)
+{
+ for (auto it = m.cbegin(), end = m.cend(); it != end; ++it)
+ s << it.key() << '\n' << it.value() << "#endif\n";
+ return s;
+}
+
+struct ModuleHeaderParameters
+{
+ AbstractMetaClassCList forwardDeclarations;
+ std::set<Include> includes;
+ ConditionalIncludeMap conditionalIncludes;
+ QString typeFunctions;
+};
+
+HeaderGenerator::IndexValues
+ HeaderGenerator::collectTypeIndexes(const AbstractMetaClassCList &classList)
+{
+ IndexValues result;
+
+ for (const auto &metaClass : classList)
+ collectClassTypeIndexes(api(), metaClass, &result);
for (const AbstractMetaEnum &metaEnum : api().globalEnums())
- writeTypeIndexValueLine(macrosStream, api(), metaEnum.typeEntry());
+ collectTypeEntryTypeIndexes(api(), metaEnum.typeEntry(), &result);
// Write the smart pointer define indexes.
int smartPointerCountIndex = getMaxTypeIndex();
int smartPointerCount = 0;
for (const auto &smp : api().instantiatedSmartPointers()) {
QString indexName = getTypeIndexVariableName(smp.type);
- _writeTypeIndexValue(macrosStream, indexName, smartPointerCountIndex);
- macrosStream << ", // " << smp.type.cppSignature() << '\n';
+ result.append({indexName, smartPointerCountIndex, smp.type.cppSignature()});
// Add a the same value for const pointees (shared_ptr<const Foo>).
const auto ptrName = smp.type.typeEntry()->entryName();
- int pos = indexName.indexOf(ptrName, 0, Qt::CaseInsensitive);
+ const auto pos = indexName.indexOf(ptrName, 0, Qt::CaseInsensitive);
if (pos >= 0) {
- indexName.insert(pos + ptrName.size() + 1, u"CONST"_s);
- _writeTypeIndexValue(macrosStream, indexName, smartPointerCountIndex);
- macrosStream << ", // (const)\n";
+ indexName.insert(pos + ptrName.size() + 1, u"const"_s);
+ result.append({indexName, smartPointerCountIndex, "(const)"_L1});
}
++smartPointerCountIndex;
++smartPointerCount;
}
+ result.append({"SBK_"_L1 + moduleName() + "_IDX_COUNT"_L1,
+ getMaxTypeIndex() + smartPointerCount, {}});
+ return result;
+}
+
+HeaderGenerator::IndexValues HeaderGenerator::collectConverterIndexes() const
+{
+ IndexValues result;
+ const auto &primitives = primitiveTypes();
+ int pCount = 0;
+ for (const auto &ptype : primitives) {
+ // Note: do not generate indices for typedef'd primitive types as
+ // they'll use the primitive type converters instead, so we
+ // don't need to create any other.
+ if (ptype->generateCode() && ptype->customConversion() != nullptr)
+ result.append({getTypeIndexVariableName(ptype), pCount++, {}});
+ }
+
+ for (const AbstractMetaType &container : api().instantiatedContainers()) {
+ result.append({getTypeIndexVariableName(container),
+ pCount++, container.cppSignature()});
+ }
+
+ // Because on win32 the compiler will not accept a zero length array.
+ if (pCount == 0)
+ pCount++;
+ result.append({"SBK_"_L1 + moduleName() + "_CONVERTERS_IDX_COUNT"_L1,
+ pCount, {}});
+ return result;
+}
+
+// PYSIDE-2404: Write the enums in unchanged case for reuse in type imports.
+// For conpatibility, we create them in uppercase, too and with
+// doubled index for emulating the former type-only case.
+//
+// FIXME: Remove in PySide 7. (See the note in `parser.py`)
+//
+static IndexValue typeIndexUpper(struct IndexValue const &ti)
+{
+ QString modi = ti.name.toUpper();
+ if (modi == ti.name)
+ modi = u"// "_s + modi;
+ return {modi, ti.value * 2, ti.comment};
+}
+
+bool HeaderGenerator::finishGeneration()
+{
+ // Generate the main header for this module. This header should be included
+ // by binding modules extending on top of this one.
+ ModuleHeaderParameters parameters;
+ ModuleHeaderParameters privateParameters;
+ StringStream macrosStream(TextStream::Language::Cpp);
+
+ const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips();
+ writeModuleCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration,
+ TypeSystem::TargetLangCode);
+
+ auto classList = api().classes();
+
+ std::sort(classList.begin(), classList.end(),
+ [](const AbstractMetaClassCPtr &a, const AbstractMetaClassCPtr &b) {
+ return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex();
+ });
+
+ const auto typeIndexes = collectTypeIndexes(classList);
- _writeTypeIndexValue(macrosStream,
- u"SBK_"_s + moduleName() + u"_IDX_COUNT"_s,
- getMaxTypeIndex() + smartPointerCount);
- macrosStream << "\n};\n";
+ macrosStream << "\n// Type indices\nenum [[deprecated]] : int {\n";
+ for (const auto &ti : typeIndexes)
+ macrosStream << typeIndexUpper(ti);
+ macrosStream << "};\n";
+
+ macrosStream << "\n// Type indices\nenum : int {\n";
+ for (const auto &ti : typeIndexes)
+ macrosStream << ti;
+ macrosStream << "};\n\n";
macrosStream << "// This variable stores all Python types exported by this module.\n";
- macrosStream << "extern PyTypeObject **" << cppApiVariableName() << ";\n\n";
+ macrosStream << "extern Shiboken::Module::TypeInitStruct *" << cppApiVariableName() << ";\n\n";
macrosStream << "// This variable stores the Python module object exported by this module.\n";
macrosStream << "extern PyObject *" << pythonModuleObjectName() << ";\n\n";
macrosStream << "// This variable stores all type converters exported by this module.\n";
@@ -448,32 +715,16 @@ bool HeaderGenerator::finishGeneration()
// TODO-CONVERTER ------------------------------------------------------------------------------
// Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex().
- macrosStream << "// Converter indices\nenum : int {\n";
- const PrimitiveTypeEntryList &primitives = primitiveTypes();
- int pCount = 0;
- for (const PrimitiveTypeEntry *ptype : primitives) {
- /* Note: do not generate indices for typedef'd primitive types
- * as they'll use the primitive type converters instead, so we
- * don't need to create any other.
- */
- if (!ptype->generateCode() || !ptype->customConversion())
- continue;
+ const auto converterIndexes = collectConverterIndexes();
+ macrosStream << "// Converter indices\nenum [[deprecated]] : int {\n";
+ for (const auto &ci : converterIndexes)
+ macrosStream << typeIndexUpper(ci);
+ macrosStream << "};\n\n";
- _writeTypeIndexValueLine(macrosStream, getTypeIndexVariableName(ptype), pCount++);
- }
-
- for (const AbstractMetaType &container : api().instantiatedContainers()) {
- _writeTypeIndexValue(macrosStream, getTypeIndexVariableName(container), pCount);
- macrosStream << ", // " << container.cppSignature() << '\n';
- pCount++;
- }
-
- // Because on win32 the compiler will not accept a zero length array.
- if (pCount == 0)
- pCount++;
- _writeTypeIndexValue(macrosStream, QStringLiteral("SBK_%1_CONVERTERS_IDX_COUNT")
- .arg(moduleName()), pCount);
- macrosStream << "\n};\n";
+ macrosStream << "// Converter indices\nenum : int {\n";
+ for (const auto &ci : converterIndexes)
+ macrosStream << ci;
+ macrosStream << "};\n";
formatTypeDefEntries(macrosStream);
@@ -481,36 +732,46 @@ bool HeaderGenerator::finishGeneration()
macrosStream << "// Macros for type check\n";
- StringStream typeFunctions(TextStream::Language::Cpp);
- StringStream privateTypeFunctions(TextStream::Language::Cpp);
- if (usePySideExtensions()) {
- typeFunctions << "QT_WARNING_PUSH\n";
- typeFunctions << "QT_WARNING_DISABLE_DEPRECATED\n";
- }
+ TextStream typeFunctions(&parameters.typeFunctions, TextStream::Language::Cpp);
+ TextStream privateTypeFunctions(&privateParameters.typeFunctions, TextStream::Language::Cpp);
+
for (const AbstractMetaEnum &cppEnum : api().globalEnums()) {
if (!cppEnum.isAnonymous()) {
- includes << cppEnum.typeEntry()->include();
+ const auto te = cppEnum.typeEntry();
+ if (te->hasConfigCondition())
+ parameters.conditionalIncludes[te->configCondition()].append(te->include());
+ else
+ parameters.includes.insert(cppEnum.typeEntry()->include());
writeSbkTypeFunction(typeFunctions, cppEnum);
}
}
StringStream protEnumsSurrogates(TextStream::Language::Cpp);
- for (auto metaClass : classList) {
- const TypeEntry *classType = metaClass->typeEntry();
+ for (const auto &metaClass : classList) {
+ const auto classType = metaClass->typeEntry();
if (!shouldGenerate(classType))
continue;
- //Includes
+ // Includes
const bool isPrivate = classType->isPrivate();
- auto &includeList = isPrivate ? privateIncludes : includes;
- includeList << classType->include();
+ auto &par = isPrivate ? privateParameters : parameters;
+ const auto classInclude = classType->include();
+ const bool hasConfigCondition = classType->hasConfigCondition();
+ if (leanHeaders() && canForwardDeclare(metaClass))
+ par.forwardDeclarations.append(metaClass);
+ else if (hasConfigCondition)
+ par.conditionalIncludes[classType->configCondition()].append(classInclude);
+ else
+ par.includes.insert(classInclude);
+
auto &typeFunctionsStr = isPrivate ? privateTypeFunctions : typeFunctions;
+ ConfigurableScope configScope(typeFunctionsStr, classType);
for (const AbstractMetaEnum &cppEnum : metaClass->enums()) {
if (cppEnum.isAnonymous() || cppEnum.isPrivate())
continue;
- EnumTypeEntry *enumType = cppEnum.typeEntry();
- includeList << enumType->include();
+ if (const auto inc = cppEnum.typeEntry()->include(); inc != classInclude)
+ par.includes.insert(inc);
writeProtectedEnumSurrogate(protEnumsSurrogates, cppEnum);
writeSbkTypeFunction(typeFunctionsStr, cppEnum);
}
@@ -520,12 +781,9 @@ bool HeaderGenerator::finishGeneration()
}
for (const auto &smp : api().instantiatedSmartPointers()) {
- const TypeEntry *classType = smp.type.typeEntry();
- includes << classType->include();
+ parameters.includes.insert(smp.type.typeEntry()->include());
writeSbkTypeFunction(typeFunctions, smp.type);
}
- if (usePySideExtensions())
- typeFunctions << "QT_WARNING_POP\n";
const QString moduleHeaderDir = outputDirectory() + u'/'
+ subDirectoryForPackage(packageName()) + u'/';
@@ -548,34 +806,40 @@ bool HeaderGenerator::finishGeneration()
}
s << "#include <sbkpython.h>\n";
+ s << "#include <sbkmodule.h>\n";
s << "#include <sbkconverter.h>\n";
QStringList requiredTargetImports = TypeDatabase::instance()->requiredTargetImports();
if (!requiredTargetImports.isEmpty()) {
s << "// Module Includes\n";
- for (const QString &requiredModule : qAsConst(requiredTargetImports))
+ for (const QString &requiredModule : std::as_const(requiredTargetImports))
s << "#include <" << getModuleHeaderFileName(requiredModule) << ">\n";
s<< '\n';
}
s << "// Bound library includes\n";
- for (const Include &include : qAsConst(includes))
+ for (const Include &include : parameters.includes)
s << include;
+ s << parameters.conditionalIncludes;
+
+ if (leanHeaders()) {
+ writeForwardDeclarations(s, parameters.forwardDeclarations);
+ } else {
+ if (!primitiveTypes().isEmpty()) {
+ s << "// Conversion Includes - Primitive Types\n";
+ const auto &primitiveTypeList = primitiveTypes();
+ for (const auto &ptype : primitiveTypeList)
+ s << ptype->include();
+ s<< '\n';
+ }
- if (!primitiveTypes().isEmpty()) {
- s << "// Conversion Includes - Primitive Types\n";
- const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes();
- for (const PrimitiveTypeEntry *ptype : primitiveTypeList)
- s << ptype->include();
- s<< '\n';
- }
-
- if (!containerTypes().isEmpty()) {
- s << "// Conversion Includes - Container Types\n";
- const ContainerTypeEntryList &containerTypeList = containerTypes();
- for (const ContainerTypeEntry *ctype : containerTypeList)
- s << ctype->include();
- s<< '\n';
+ if (!containerTypes().isEmpty()) {
+ s << "// Conversion Includes - Container Types\n";
+ const ContainerTypeEntryCList &containerTypeList = containerTypes();
+ for (const auto &ctype : containerTypeList)
+ s << ctype->include();
+ s<< '\n';
+ }
}
s << macrosStream.toString() << '\n';
@@ -585,25 +849,21 @@ bool HeaderGenerator::finishGeneration()
<< protEnumsSurrogates.toString() << '\n';
}
- s << "namespace Shiboken\n{\n\n"
- << "// PyType functions, to get the PyObjectType for a type T\n"
- << typeFunctions.toString() << '\n'
- << "} // namespace Shiboken\n\n"
- << "#endif // " << includeShield << "\n\n";
+ writeTypeFunctions(s, parameters.typeFunctions);
+
+ s << "#endif // " << includeShield << "\n\n";
file.done();
- if (hasPrivateClasses()) {
- writePrivateHeader(moduleHeaderDir, includeShield,
- privateIncludes, privateTypeFunctions.toString());
- }
+ if (hasPrivateClasses())
+ writePrivateHeader(moduleHeaderDir, includeShield, privateParameters);
+
return true;
}
void HeaderGenerator::writePrivateHeader(const QString &moduleHeaderDir,
const QString &publicIncludeShield,
- const QSet<Include> &privateIncludes,
- const QString &privateTypeFunctions)
+ const ModuleHeaderParameters &parameters)
{
// Write includes and type functions of private classes
@@ -611,63 +871,90 @@ void HeaderGenerator::writePrivateHeader(const QString &moduleHeaderDir,
TextStream &ps = privateFile.stream;
ps.setLanguage(TextStream::Language::Cpp);
QString privateIncludeShield =
- publicIncludeShield.left(publicIncludeShield.size() - 2)
- + QStringLiteral("_P_H");
+ publicIncludeShield.left(publicIncludeShield.size() - 2) + "_P_H"_L1;
ps << licenseComment()<< "\n\n";
ps << "#ifndef " << privateIncludeShield << '\n';
ps << "#define " << privateIncludeShield << "\n\n";
- for (const Include &include : qAsConst(privateIncludes))
+ for (const Include &include : parameters.includes)
ps << include;
+ ps << parameters.conditionalIncludes;
ps << '\n';
+ if (leanHeaders())
+ writeForwardDeclarations(ps, parameters.forwardDeclarations);
+
+ writeTypeFunctions(ps, parameters.typeFunctions);
+
+ ps << "#endif\n";
+ privateFile.done();
+}
+
+void HeaderGenerator::writeTypeFunctions(TextStream &s, const QString &typeFunctions)
+{
+ if (typeFunctions.isEmpty())
+ return;
+
if (usePySideExtensions())
- ps << "QT_WARNING_PUSH\nQT_WARNING_DISABLE_DEPRECATED\n";
+ s << "QT_WARNING_PUSH\nQT_WARNING_DISABLE_DEPRECATED\n";
- ps << "namespace Shiboken\n{\n\n"
+ s << "namespace Shiboken\n{\n\n"
<< "// PyType functions, to get the PyObjectType for a type T\n"
- << privateTypeFunctions << '\n'
+ << typeFunctions << '\n'
<< "} // namespace Shiboken\n\n";
if (usePySideExtensions())
- ps << "QT_WARNING_POP\n";
-
- ps << "#endif\n";
- privateFile.done();
+ s << "QT_WARNING_POP\n";
}
-void HeaderGenerator::writeProtectedEnumSurrogate(TextStream &s, const AbstractMetaEnum &cppEnum) const
+void HeaderGenerator::writeProtectedEnumSurrogate(TextStream &s, const AbstractMetaEnum &cppEnum)
{
if (avoidProtectedHack() && cppEnum.isProtected())
s << "enum " << protectedEnumSurrogateName(cppEnum) << " {};\n";
}
-void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum &cppEnum) const
+void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum &cppEnum)
{
const QString enumName = avoidProtectedHack() && cppEnum.isProtected()
? protectedEnumSurrogateName(cppEnum)
: cppEnum.qualifiedCppName();
+ const auto te = cppEnum.typeEntry();
+ ConfigurableScope configScope(s, te);
+ s << "template<> inline PyTypeObject *SbkType< " << m_gsp << enumName << " >() ";
+ s << "{ return " << cpythonTypeNameExt(te) << "; }\n";
- s << "template<> inline PyTypeObject *SbkType< ::" << enumName << " >() ";
- s << "{ return " << cpythonTypeNameExt(cppEnum.typeEntry()) << "; }\n";
-
- FlagsTypeEntry *flag = cppEnum.typeEntry()->flags();
+ const auto flag = cppEnum.typeEntry()->flags();
if (flag) {
- s << "template<> inline PyTypeObject *SbkType< ::" << flag->name() << " >() "
+ s << "template<> inline PyTypeObject *SbkType< " << m_gsp << flag->name() << " >() "
<< "{ return " << cpythonTypeNameExt(flag) << "; }\n";
}
}
-void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaClass *cppClass)
+void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaClassCPtr &cppClass)
{
- s << "template<> inline PyTypeObject *SbkType< ::" << cppClass->qualifiedCppName() << " >() "
- << "{ return reinterpret_cast<PyTypeObject *>(" << cpythonTypeNameExt(cppClass->typeEntry()) << "); }\n";
+ s << "template<> inline PyTypeObject *SbkType< "
+ << getFullTypeName(cppClass) << " >() "
+ << "{ return " << cpythonTypeNameExt(cppClass->typeEntry()) << "; }\n";
}
void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaType &metaType)
{
- s << "template<> inline PyTypeObject *SbkType< ::" << metaType.cppSignature() << " >() "
+ s << "template<> inline PyTypeObject *SbkType< "
+ << m_gsp << metaType.cppSignature() << " >() "
<< "{ return " << cpythonTypeNameExt(metaType) << "; }\n";
}
+
+void HeaderGenerator::writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ if (!codeSnips.isEmpty()) {
+ try {
+ writeCodeSnips(s, codeSnips, position, language);
+ } catch (const std::exception &e) {
+ throw Exception(msgSnippetError("module header of "_L1 + moduleName(), e.what()));
+ }
+ }
+}