summaryrefslogtreecommitdiffstats
path: root/cppgenerator.cpp
diff options
context:
space:
mode:
authorMarcelo Lira <marcelo.lira@openbossa.org>2009-08-17 19:31:37 -0300
committerMarcelo Lira <marcelo.lira@openbossa.org>2009-08-17 19:31:37 -0300
commite0c29962e6f334452f0c9db2caaf6ed18065de85 (patch)
treecee27801c196fbcacf6130ad64216af133b555dd /cppgenerator.cpp
downloadshiboken-e0c29962e6f334452f0c9db2caaf6ed18065de85.tar.gz
shiboken-e0c29962e6f334452f0c9db2caaf6ed18065de85.tar.xz
shiboken-e0c29962e6f334452f0c9db2caaf6ed18065de85.zip
The End Is the Beginning Is the End
Diffstat (limited to 'cppgenerator.cpp')
-rw-r--r--cppgenerator.cpp1453
1 files changed, 1453 insertions, 0 deletions
diff --git a/cppgenerator.cpp b/cppgenerator.cpp
new file mode 100644
index 00000000..a0de62bb
--- /dev/null
+++ b/cppgenerator.cpp
@@ -0,0 +1,1453 @@
+/*
+ * This file is part of the Shiboken Python Binding Generator project.
+ *
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: PySide team <contact@pyside.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "cppgenerator.h"
+#include <apiextractor/reporthandler.h>
+
+#include <QtCore/QDir>
+#include <QtCore/QTextStream>
+#include <QtCore/QDebug>
+
+static Indentor INDENT;
+
+QString CppGenerator::cpythonWrapperCPtr(const AbstractMetaClass* metaClass, QString argName)
+{
+ return QString("%1_cptr(%2)").arg(cpythonBaseName(metaClass->typeEntry())).arg(argName);
+}
+
+QString CppGenerator::fileNameForClass(const AbstractMetaClass *metaClass) const
+{
+ return metaClass->qualifiedCppName().toLower() + QLatin1String("_wrapper.cpp");
+}
+
+QList<AbstractMetaFunctionList> CppGenerator::filterGroupedFunctions(const AbstractMetaClass* metaClass)
+{
+ AbstractMetaFunctionList lst;
+ if (metaClass)
+ lst = queryFunctions(metaClass, true);
+ else
+ lst = globalFunctions();
+
+ QMap<QString, AbstractMetaFunctionList> results;
+ foreach (AbstractMetaFunction* func, lst) {
+ //skip signals
+ if (func->isSignal() || func->isDestructor() || (func->isModifiedRemoved() && !func->isAbstract()))
+ continue;
+ if (!results.contains(func->name()))
+ results[func->name()] = AbstractMetaFunctionList();
+ results[func->name()].append(func);
+ }
+
+ //TODO: put these lines back to work
+
+ //append global operators
+ //lst += queryGlobalOperators(metaClass);
+
+ return results.values();
+}
+
+QList<AbstractMetaFunctionList> CppGenerator::filterGroupedOperatorFunctions(const AbstractMetaClass* metaClass,
+ uint query)
+{
+ QMap<QPair<QString, QPair<int, bool> >, AbstractMetaFunctionList> results;
+ foreach (AbstractMetaFunction* func, metaClass->operatorOverloads(query)) {
+ if (func->isModifiedRemoved())
+ continue;
+ int args;
+ bool revOp;
+ if (func->isComparisonOperator()) {
+ args = -1;
+ revOp = false;
+ } else {
+ args = func->arguments().size();
+ revOp = ShibokenGenerator::isReverseOperator(func);
+ }
+ QPair<QString, QPair<int, bool> > op(func->name(),
+ QPair<int, bool>(args, revOp));
+ if (!results.contains(op))
+ results[op] = AbstractMetaFunctionList();
+ results[op].append(func);
+ }
+ return results.values();
+}
+
+/*!
+ Function used to write the class generated binding code on the buffer
+ \param s the output buffer
+ \param metaClass the pointer to metaclass information
+*/
+void CppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ ReportHandler::debugSparse("Generating wrapper implementation for " + metaClass->fullName());
+
+ // write license comment
+ s << licenseComment() << endl;
+
+ if (!metaClass->isNamespace()) {
+ //workaround to access protected functions
+ s << "//workaround to access protected functions" << endl;
+ s << "#define protected public" << endl << endl;
+ }
+
+ // headers
+ s << "// default includes" << endl;
+ s << "#include <shiboken.h>" << endl;
+ s << "#include \"" << moduleName().toLower() << "_python.h\"" << endl << endl;
+
+ QString headerfile = fileNameForClass(metaClass);
+ headerfile.replace("cpp", "h");
+ s << "#include \"" << headerfile << '"' << endl;
+ foreach (AbstractMetaClass* innerClass, metaClass->innerClasses()) {
+ if (shouldGenerate(innerClass)) {
+ QString headerfile = fileNameForClass(metaClass);
+ headerfile.replace("cpp", "h");
+ s << "#include \"" << headerfile << '"' << endl;
+ }
+ }
+
+ //Extra includes
+ s << endl << "// Extra includes" << endl;
+ QList<Include> includes = metaClass->typeEntry()->extraIncludes();
+ qSort(includes.begin(), includes.end());
+ foreach (Include inc, includes)
+ s << inc.toString() << endl;
+ s << endl;
+
+ if (metaClass->typeEntry()->typeFlags() & ComplexTypeEntry::Deprecated)
+ s << "#Deprecated" << endl;
+
+ if (!canCreateWrapperFor(metaClass))
+ return;
+
+ s << "using namespace Shiboken;" << endl << endl;
+
+ if (!metaClass->isNamespace()) {
+ s << "// Native ---------------------------------------------------------" << endl << endl;
+
+ //inject code native beginner
+ writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::Beginning, TypeSystem::NativeCode);
+
+ foreach (const AbstractMetaFunction *func, filterFunctions(metaClass)) {
+ if (func->isPrivate() || (func->isModifiedRemoved() && !func->isAbstract()))
+ continue;
+
+ if (func->isConstructor() && !func->isCopyConstructor()) {
+ writeConstructorNative(s, func);
+ } else if (func->isVirtual() || func->isAbstract()) {
+ writeVirtualMethodNative(s, func);
+// } else if (func->hasInjectedCodeOrSignatureModifications() ||
+// func->isThread() || func->allowThread()) {
+// writeNonVirtualModifiedFunctionNative(s, func);
+ }
+ }
+
+ writeDestructorNative(s, metaClass);
+
+ //inject code native end
+ writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::End, TypeSystem::NativeCode);
+
+ s << endl << "// Target ---------------------------------------------------------" << endl << endl;
+ }
+
+ Indentation indentation(INDENT);
+
+ QString methodsDefinitions;
+ QTextStream md(&methodsDefinitions);
+
+ bool hasComparisonOperator = false;
+ bool typeAsNumber = false;
+
+ foreach (AbstractMetaFunctionList allOverloads, filterGroupedFunctions(metaClass)) {
+ AbstractMetaFunctionList overloads;
+ foreach (AbstractMetaFunction* func, allOverloads) {
+ if (!func->isModifiedRemoved() && !func->isPrivate() &&
+ func->ownerClass() == func->implementingClass())
+ overloads.append(func);
+ }
+
+ if (overloads.isEmpty())
+ continue;
+
+ const AbstractMetaFunction* rfunc = overloads[0];
+ if (rfunc->isConstructor())
+ writeConstructorWrapper(s, overloads);
+ else if (rfunc->isArithmeticOperator()
+ || rfunc->isLogicalOperator()
+ || rfunc->isBitwiseOperator())
+ typeAsNumber = true;
+ else if (rfunc->isComparisonOperator())
+ hasComparisonOperator = true;
+ else
+ writeMethodWrapper(s, overloads);
+
+ if (!rfunc->isConstructor() && !rfunc->isOperatorOverload())
+ writeMethodDefinition(md, overloads);
+ }
+
+ // Write methods definition
+ s << "static PyMethodDef Py" << metaClass->qualifiedCppName() << "_methods[] = {" << endl;
+ s << methodsDefinitions << INDENT << "{0} // Sentinel" << endl << "};" << endl << endl;
+
+ if (typeAsNumber) {
+ QList<AbstractMetaFunctionList> opOverloads = filterGroupedOperatorFunctions(
+ metaClass,
+ AbstractMetaClass::ArithmeticOp
+ | AbstractMetaClass::LogicalOp
+ | AbstractMetaClass::BitwiseOp);
+
+ foreach (AbstractMetaFunctionList allOverloads, opOverloads) {
+ AbstractMetaFunctionList overloads;
+ foreach (AbstractMetaFunction* func, allOverloads) {
+ if (!func->isModifiedRemoved()
+ && !func->isPrivate()
+ && func->ownerClass() == func->implementingClass())
+ overloads.append(func);
+ }
+
+ if (overloads.isEmpty())
+ continue;
+
+ writeMethodWrapper(s, overloads);
+ }
+
+ s << "// type has number operators" << endl;
+ writeTypeAsNumberDefinition(s, metaClass);
+ }
+
+ if (hasComparisonOperator) {
+ s << "// Rich comparison" << endl;
+ writeRichCompareFunction(s, metaClass);
+ }
+
+ s << "extern \"C\"" << endl << '{' << endl << endl;
+ writeClassDefinition(s, metaClass);
+ s << endl;
+
+ foreach (AbstractMetaEnum* cppEnum, metaClass->enums())
+ writeEnumDefinition(s, cppEnum);
+ s << endl;
+
+ writeClassRegister(s, metaClass);
+ s << endl << "} // extern \"C\"" << endl;
+}
+
+void CppGenerator::writeConstructorNative(QTextStream& s, const AbstractMetaFunction* func)
+{
+ Indentation indentation(INDENT);
+ s << functionSignature(func, wrapperName(func->ownerClass()) + "::", "",
+ (Option)(OriginalTypeDescription | SkipDefaultValues));
+ s << " : ";
+ writeFunctionCall(s, func);
+ s << " {" << endl;
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::All, func);
+ s << INDENT << "// ... middle" << endl;
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func);
+ s << '}' << endl << endl;
+}
+
+void CppGenerator::writeDestructorNative(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ s << wrapperName(metaClass) << "::~" << wrapperName(metaClass) << "()" << endl << '{' << endl;
+ s << '}' << endl;
+}
+
+void CppGenerator::writeVirtualMethodNative(QTextStream &s, const AbstractMetaFunction* func)
+{
+ QString returnKeyword = func->type() ? QLatin1String("return ") : QString();
+ QString prefix = wrapperName(func->ownerClass()) + "::";
+ s << functionSignature(func, prefix, "", Generator::SkipDefaultValues) << endl << "{" << endl;
+
+ Indentation indentation(INDENT);
+
+ if (func->hasInjectedCode()) {
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::NativeCode, func);
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::NativeCode, func);
+ } else if (func->isAbstract() && func->isModifiedRemoved()) {
+ s << INDENT << "#error Pure virtual method \"" << func->ownerClass()->name();
+ s << "::" << func->minimalSignature();
+ s << "\" must be implement but was completely removed on typesystem." << endl;
+ } else {
+// QString converter_calls;
+// QTextStream s_converter_calls(&converter_calls);
+// QString callDefault = returnKeyword + "default_method(self";
+// foreach (const AbstractMetaArgument* arg, func->arguments()) {
+// Indentation indentation(INDENT);
+// callDefault += ", " + arg->argumentName();
+// s_converter_calls << INDENT << ", " << '&' << arg->argumentName()
+// if ((arg->type()->isQObject() || arg->type()->isObject() || arg->type()->isValue()) &&
+// !arg->type()->isReference()) {
+// s_converter_calls << '&' << arg->argumentName() << ", nagasaki::to_python_converter" << endl;
+// } else {
+// s_converter_calls << "nagasaki::convert_to_python< "
+// << argumentString(func, arg, SkipName | SkipDefaultValues)
+// << " >(" << arg->argumentName() << ')' << endl;
+// }
+// }
+// callDefault += ");";
+ if (func->allowThread())
+ s << INDENT << "// how to say to Python to allow threads?" << endl;
+
+ s << INDENT << "PyObject* method = BindingManager::instance().getOverride(this, \"";
+ s << func->name() << "\");" << endl;
+
+ s << INDENT << "if (!method) {" << endl;
+ {
+ Indentation indentation(INDENT);
+ s << INDENT;
+ if (func->isAbstract()) {
+ s << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '";
+ s << func->ownerClass()->name() << '.' << func->name();
+ s << "()' not implemented.\");" << endl;
+ s << INDENT << "return";
+ } else {
+ s << "return this->" << func->implementingClass()->qualifiedCppName() << "::";
+ writeFunctionCall(s, func);
+ }
+ }
+ s << ';' << endl << INDENT << '}' << endl << endl;
+
+ s << INDENT << "PyObject* args = ";
+ if (func->arguments().isEmpty()) {
+ s << "PyTuple_New(0);" << endl;
+ } else {
+ s << "Py_BuildValue(\"(" << getFormatUnitString(func->arguments()) << ")\"";
+ foreach (const AbstractMetaArgument* arg, func->arguments()) {
+ s << ", " << arg->argumentName();
+ if ((arg->type()->isObject() || arg->type()->isValue()) && !arg->type()->isReference()) {
+ s << ", Shiboken::Converter< "
+ << translateType(arg->type(), func->ownerClass())
+ << " >::toPython";
+ }
+ }
+ s << ");" << endl;
+ }
+ s << endl;
+
+ s << INDENT << "PyGILState_STATE gil_state = PyGILState_Ensure();" << endl;
+
+ s << INDENT;
+ if (!returnKeyword.isEmpty())
+ s << "PyObject* method_result = ";
+ s << "PyObject_Call(method, args, NULL);" << endl;
+ s << INDENT << "PyGILState_Release(gil_state);" << endl << endl;
+ s << INDENT << "Py_XDECREF(args);" << endl;
+ s << INDENT << "Py_XDECREF(method);" << endl;
+
+ s << endl << INDENT << "// check and set Python error here..." << endl;
+ }
+
+ if (!returnKeyword.isEmpty()) {
+ s << INDENT << returnKeyword;
+ writeToCppConversion(s, func->type(), func->implementingClass(), "method_result");
+ s << ';' << endl;
+ }
+ s << '}' << endl << endl;
+}
+
+void CppGenerator::writeNonVirtualModifiedFunctionNative(QTextStream& s, const AbstractMetaFunction* func)
+{
+ Indentation indentation(INDENT);
+
+ s << getFunctionReturnType(func) << ' ';
+ s << func->ownerClass()->name() << '_' << func->originalName() << "_modified(";
+
+ uint options = SkipRemovedArguments | SkipDefaultValues;
+ if (!func->isStatic())
+ options |= WriteSelf;
+
+ writeFunctionArguments(s, func, options);
+ s << ")" << endl << "{" << endl;
+
+ if (func->isThread())
+ s << INDENT << "thread_locker lock;" << endl;
+
+ if (func->allowThread())
+ s << INDENT << "py_allow_threads allow_threads;" << endl;
+
+ if (!getCodeSnips(func).isEmpty()) {
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::All, func);
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func);
+ } else {
+ s << INDENT;
+ if (func->type())
+ s << "return ";
+ if (func->isStatic())
+ s << func->declaringClass()->name() << "::";
+ else
+ s << "self.";
+ writeFunctionCall(s, func);
+ s << ";" << endl;
+ }
+
+ s << '}' << endl << endl;
+}
+
+void CppGenerator::writeConstructorWrapper(QTextStream& s, const AbstractMetaFunctionList overloads)
+{
+ PolymorphicData polymorphicData(overloads);
+ const AbstractMetaFunction* rfunc = polymorphicData.referenceFunction();
+ QString className = rfunc->ownerClass()->qualifiedCppName();
+
+ s << "PyObject*" << endl;
+ s << cpythonFunctionName(rfunc) << "(PyTypeObject *type, PyObject *args, PyObject *kwds)" << endl;
+ s << '{' << endl;
+
+ s << INDENT << "PyObject* self;" << endl;
+ s << INDENT << getFunctionReturnType(rfunc) << " cptr;" << endl << endl;
+
+ if (rfunc->ownerClass()->isAbstract()) {
+ s << INDENT << "if (type == &Py" << className << "_Type) {" << endl;
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyErr_SetString(PyExc_NotImplementedError," << endl;
+ s << INDENT << INDENT << "\"'" << className;
+ s << "' represents a C++ abstract class and cannot be instanciated\");" << endl;
+ s << INDENT << "return 0;" << endl;
+ }
+ s << INDENT << '}' << endl << endl;
+ }
+
+ s << INDENT << "if (!PyType_IsSubtype(type, &Py" << className << "_Type))" << endl;
+ s << INDENT << INDENT << "return 0;" << endl << endl;
+
+ if (polymorphicData.maxArgs() > 0) {
+ s << endl << INDENT << "int numArgs = ";
+ writeArgumentsInitializer(s, polymorphicData);
+ }
+
+ writeCodeSnips(s, getCodeSnips(rfunc), CodeSnip::Beginning, TypeSystem::All, rfunc);
+
+ writePolymorphicDecisor(s, &polymorphicData);
+ s << endl;
+
+ s << INDENT << "self = Shiboken::PyBaseWrapper_New(type, &Py" << className << "_Type, cptr);" << endl;
+ s << endl << INDENT << "if (!self) {" << endl;
+ {
+ Indentation indentation(INDENT);
+ s << INDENT << "if (cptr) delete cptr;" << endl;
+ s << INDENT << "return 0;" << endl;
+ }
+ s << INDENT << '}' << endl;
+
+ writeCodeSnips(s, getCodeSnips(rfunc), CodeSnip::End, TypeSystem::All, rfunc);
+
+ s << endl << INDENT << "return self;" << endl;
+ if (polymorphicData.maxArgs() > 0)
+ writeErrorSection(s, polymorphicData);
+ s << '}' << endl << endl;
+}
+
+void CppGenerator::writeMethodWrapper(QTextStream& s, const AbstractMetaFunctionList overloads)
+{
+ PolymorphicData polymorphicData(overloads);
+ const AbstractMetaFunction* rfunc = polymorphicData.referenceFunction();
+
+ //DEBUG
+ //if (rfunc->isOperatorOverload()) {
+ // QString dumpFile = QString("%1_%2.dot").arg(m_packageName).arg(pythonOperatorFunctionName(rfunc)).toLower();
+ // polymorphicData.dumpGraph(dumpFile);
+ //}
+ //DEBUG
+
+ // TODO: take this off when operator generation is fixed
+ // if (rfunc->isOperatorOverload())
+// if (rfunc->isInplaceOperator())
+// s << "/*" << endl;
+
+ int minArgs = polymorphicData.minArgs();
+ int maxArgs = polymorphicData.maxArgs();
+ if (ShibokenGenerator::isReverseOperator(rfunc)) {
+ minArgs--;
+ maxArgs--;
+ }
+
+ s << "static PyObject*" << endl;
+ s << cpythonFunctionName(rfunc) << "(PyObject* self";
+ if (maxArgs > 0) {
+ s << ", PyObject* arg";
+ if (maxArgs > 1)
+ s << 's';
+ }
+ s << ')' << endl << '{' << endl;
+
+ if (rfunc->isAbstract()) {
+ s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '";
+ s << rfunc->ownerClass()->name() << '.' << rfunc->name();
+ s << "()' not implemented.\");" << endl;
+ s << INDENT << "return 0;" << endl;
+ } else {
+ if (rfunc->implementingClass() &&
+ (!rfunc->implementingClass()->isNamespace() && !rfunc->isStatic())) {
+ // Checks if the underlying C++ object is valid.
+ // If the wrapped C++ library have no function that steals ownership and
+ // deletes the C++ object this check would not be needed.
+ s << INDENT << "if (!Shiboken::cppObjectIsValid((Shiboken::PyBaseWrapper*)self))" << endl;
+ s << INDENT << INDENT << "return 0;" << endl << endl;
+ }
+
+ if (rfunc->type())
+ s << INDENT << getFunctionReturnType(rfunc) << ' ' << retvalVariableName() << ';' << endl;
+
+ if (minArgs != maxArgs || maxArgs > 1) {
+ s << INDENT << "int numArgs = ";
+ if (minArgs == 0 && maxArgs == 1)
+ s << "(arg == 0 ? 0 : 1);" << endl;
+ else
+ writeArgumentsInitializer(s, polymorphicData);
+ }
+
+ writePolymorphicDecisor(s, &polymorphicData);
+
+ s << endl << INDENT << "if (PyErr_Occurred())" << endl;
+ s << INDENT << INDENT << "return 0;" << endl;
+
+ s << INDENT;
+ if (rfunc->type()) {
+ s << "return ";
+ if (rfunc->isInplaceOperator())
+ s << "self";
+ else
+ writeToPythonConversion(s, rfunc->type(), rfunc->ownerClass(), retvalVariableName());
+ } else {
+ s << "Py_RETURN_NONE";
+ }
+ s << ';' << endl;
+
+ if (maxArgs > 0)
+ writeErrorSection(s, polymorphicData);
+ }
+ s << '}' << endl << endl;
+
+ // TODO: take this off when operator generation is fixed
+// if (rfunc->isOperatorOverload())
+// if (rfunc->isInplaceOperator())
+// s << "*/" << endl;
+}
+
+void CppGenerator::writeArgumentsInitializer(QTextStream& s, PolymorphicData& polymorphicData)
+{
+ const AbstractMetaFunction* rfunc = polymorphicData.referenceFunction();
+ s << "PyTuple_GET_SIZE(args);" << endl;
+
+ s << INDENT << "PyObject* pyargs[] = {";
+ s << QString(polymorphicData.maxArgs(), '0').split("", QString::SkipEmptyParts).join(", ");
+ s << "};" << endl << endl;
+
+ QStringList palist;
+ for (int i = 0; i < polymorphicData.maxArgs(); i++)
+ palist << QString("&(pyargs[%1])").arg(i);
+ QString pyargs = palist.join(", ");
+
+ QList<int> invalidArgsLength = polymorphicData.invalidArgumentLengths();
+ if (!invalidArgsLength.isEmpty()) {
+ QStringList invArgsLen;
+ foreach (int i, invalidArgsLength)
+ invArgsLen << QString("numArgs == %1").arg(i);
+ s << INDENT << "// invalid argument lengths" << endl;
+ s << INDENT << "if (" << invArgsLen.join(" || ") << ")" << endl;
+ s << INDENT << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;" << endl << endl;
+ }
+
+ QString funcName;
+ if (rfunc->isOperatorOverload())
+ funcName = ShibokenGenerator::pythonOperatorFunctionName(rfunc);
+ else
+ funcName = rfunc->name();
+
+ s << INDENT << "if (!PyArg_UnpackTuple(args, \"" << funcName << "\", ";
+ s << polymorphicData.minArgs() << ", " << polymorphicData.maxArgs();
+ s << ", " << pyargs << "))" << endl;
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "return 0;" << endl;
+ }
+ s << endl;
+}
+
+void CppGenerator::writeErrorSection(QTextStream& s, PolymorphicData& polymorphicData)
+{
+ const AbstractMetaFunction* rfunc = polymorphicData.referenceFunction();
+ s << endl << INDENT << cpythonFunctionName(rfunc) << "_TypeError:" << endl;
+ Indentation indentation(INDENT);
+ QString funcName;
+ if (rfunc->isOperatorOverload())
+ funcName = ShibokenGenerator::pythonOperatorFunctionName(rfunc);
+ else
+ funcName = rfunc->name();
+ s << INDENT << "PyErr_SetString(PyExc_TypeError, \"'" << funcName;
+ s << "()' called with wrong parameters.\");" << endl;
+ s << INDENT << "return 0;" << endl;
+}
+
+void CppGenerator::writeTypeCheck(QTextStream& s, const PolymorphicData* polyData, QString argumentName)
+{
+ const AbstractMetaType* argType = polyData->argType();
+ AbstractMetaFunctionList implicitConverters;
+ if (argType->isValue()) {
+ const AbstractMetaClass* metaClass = classes().findClass(argType->name());
+ if (metaClass)
+ implicitConverters = metaClass->implicitConversions();
+ }
+
+ int alternativeNumericTypes = 0;
+ foreach (PolymorphicData* pd, polyData->polymorphicDataOnPosition(polyData->argPos())) {
+ if (!pd->argType()->isPrimitive())
+ continue;
+ if (ShibokenGenerator::isNumber(pd->argType()->typeEntry()))
+ alternativeNumericTypes++;
+ }
+
+ // This condition trusts that the PolymorphicData object will arrange for
+ // PyInt type to be the last entry on a list of polymorphic argument data.
+ bool numberType = alternativeNumericTypes == 1 || ShibokenGenerator::isPyInt(argType);
+
+ if (implicitConverters.size() > 0)
+ s << '(';
+
+ s << cpythonCheckFunction(argType, numberType) << '(' << argumentName << ')';
+
+ foreach (const AbstractMetaFunction* ctor, implicitConverters) {
+ s << " || ";
+ s << cpythonCheckFunction(ctor->arguments().first()->type(), numberType);
+ s << '(' << argumentName << ')';
+ }
+
+ if (implicitConverters.size() > 0)
+ s << ')';
+}
+
+void CppGenerator::writePolymorphicDecisor(QTextStream& s, PolymorphicData* parentPolymorphicData)
+{
+ bool hasDefaultCall = parentPolymorphicData->nextArgumentHasDefaultValue();
+ if (!hasDefaultCall && parentPolymorphicData->isHeadPolymorphicData()) {
+ foreach (const AbstractMetaFunction* func, parentPolymorphicData->overloads()) {
+ if (parentPolymorphicData->isFinalOccurrence(func)) {
+ hasDefaultCall = true;
+ break;
+ }
+ }
+ }
+
+ const AbstractMetaFunction* rfunc = parentPolymorphicData->referenceFunction();
+
+ int minArgs = parentPolymorphicData->minArgs();
+ int maxArgs = parentPolymorphicData->maxArgs();
+ if (ShibokenGenerator::isReverseOperator(rfunc)) {
+ minArgs--;
+ maxArgs--;
+ }
+
+ if (maxArgs == 0
+ || (!parentPolymorphicData->isHeadPolymorphicData()
+ && (parentPolymorphicData->nextPolymorphicData().isEmpty()
+ || (!hasDefaultCall && parentPolymorphicData->overloads().size() == 1)))) {
+ const AbstractMetaFunction* func = parentPolymorphicData->overloads()[0];
+ int removed = PolymorphicData::numberOfRemovedArguments(func);
+ writeMethodCall(s, func, func->arguments().size() - removed);
+ return;
+ }
+
+ bool varargs = maxArgs > 1 || rfunc->isConstructor();
+
+ s << INDENT;
+
+ // can make a default call
+ if (hasDefaultCall) {
+ s << "if (numArgs == " << parentPolymorphicData->argPos() + 1 << ") { // hasDefaultCall" << endl;
+ {
+ Indentation indent(INDENT);
+ writeMethodCall(s, rfunc, parentPolymorphicData->argPos() + 1);
+ }
+ s << INDENT << "} else ";
+ }
+
+ // last occurrence of function signature
+ if (!parentPolymorphicData->isHeadPolymorphicData()) {
+ foreach (const AbstractMetaFunction* func, parentPolymorphicData->overloads()) {
+ if (parentPolymorphicData->isFinalOccurrence(func)) {
+ int lastArg = parentPolymorphicData->argPos() + 1;
+ s << "if (numArgs == " << lastArg << ") { // final:" << func->minimalSignature() << endl;
+ {
+ Indentation indent(INDENT);
+ writeMethodCall(s, func, lastArg);
+ }
+ s << INDENT << "} else ";
+ }
+ }
+ }
+
+ foreach (PolymorphicData* polymorphicData, parentPolymorphicData->nextPolymorphicData()) {
+ if (maxArgs > 0) {
+ bool signatureFound = polymorphicData->overloads().size() == 1 &&
+ !polymorphicData->nextArgumentHasDefaultValue();
+ const AbstractMetaFunction* func = polymorphicData->overloads()[0];
+ QString pyArgName = varargs ? QString("pyargs[%1]").arg(polymorphicData->argPos()) : "arg";
+
+ s << "if (";
+ if (signatureFound && varargs) {
+ s << "numArgs == ";
+ s << func->arguments().size() - PolymorphicData::numberOfRemovedArguments(func);
+ s << " && ";
+ }
+
+ writeTypeCheck(s, polymorphicData, pyArgName);
+
+ if (polymorphicData->argType()->isContainer() &&
+ ((ContainerTypeEntry*)polymorphicData->argType()->typeEntry())->type()
+ == ContainerTypeEntry::PairContainer) {
+ s << " && PyTuple_GET_SIZE(" << pyArgName << ") == 2";
+ }
+
+ if (signatureFound && varargs) {
+ int numArgs = func->arguments().size() - PolymorphicData::numberOfRemovedArguments(func);
+ PolymorphicData* tmp = polymorphicData;
+ for (int i = polymorphicData->argPos() + 1; i < numArgs; i++) {
+ tmp = tmp->nextPolymorphicData()[0];
+ s << " && ";
+ writeTypeCheck(s, tmp, QString("pyargs[%1]").arg(i));
+ }
+ }
+ s << ") {" << endl;
+ {
+ Indentation indent(INDENT);
+ int allRemoved = PolymorphicData::numberOfRemovedArguments(func);
+ int lastArg = signatureFound ? func->arguments().size() - allRemoved
+ : polymorphicData->argPos() + 1;
+ int removed = 0;
+ for (int i = polymorphicData->argPos(); i < lastArg; i++) {
+ if (func->argumentRemoved(i + 1))
+ removed++;
+ QString argName = QString("cpp_arg%1").arg(i);
+ if (varargs)
+ pyArgName = QString("pyargs[%1]").arg(i);
+ const AbstractMetaType* type = func->arguments()[i + removed]->type();
+ s << INDENT << translateType(type, func->implementingClass()) << ' ' << argName << " = ";
+ writeToCppConversion(s, type, func->implementingClass(), pyArgName);
+ s << ';' << endl;
+ }
+ }
+ }
+
+ {
+ Indentation indent(INDENT);
+ writePolymorphicDecisor(s, polymorphicData);
+ }
+
+ s << INDENT << "} else ";
+ }
+ if (maxArgs > 0)
+ s << "goto " << cpythonFunctionName(rfunc) << "_TypeError;" << endl;
+}
+
+void CppGenerator::writeMethodCall(QTextStream& s, const AbstractMetaFunction* func, int lastArg)
+{
+ s << INDENT << "// " << func->minimalSignature() << endl;
+
+ bool writeCall = true;
+ if (func->hasSignatureModifications()) {
+ CodeSnipList snips = getCodeSnips(func);
+ foreach (CodeSnip snip, snips) {
+ if (snip.position == CodeSnip::Beginning) {
+ // modified signature was provided with custom code for the method call
+ writeCall = false;
+ break;
+ }
+ }
+ writeCodeSnips(s, snips, CodeSnip::Beginning, TypeSystem::All, func);
+ }
+
+ if (writeCall) {
+ bool badModifications = false;
+ QStringList userArgs;
+ if (!func->isCopyConstructor()) {
+ int removed = 0;
+ for (int i = 0; i < lastArg + removed; i++) {
+ const AbstractMetaArgument* arg = func->arguments()[i];
+ if (func->argumentRemoved(i + 1)) {
+ // If some argument with default value is removed from a
+ // method signature, the said value must be explicitly
+ // added to the method call.
+ removed++;
+ if (arg->defaultValueExpression().isEmpty())
+ badModifications = true;
+ else
+ userArgs << arg->defaultValueExpression();
+ } else {
+ userArgs << QString("cpp_arg%1").arg(arg->argumentIndex() - removed);
+ }
+ }
+
+ // If any argument's default value was modified the method must be called
+ // with this new value whenever the user doesn't pass a explicit value to it.
+ // Also, any unmodified default value coming after the last user specified
+ // argument and before the modified argument must be splicitly stated.
+ QStringList otherArgs;
+ bool defaultModified = false;
+ for (int i = func->arguments().size() - 1; i >= lastArg; i--) {
+ const AbstractMetaArgument* arg = func->arguments()[i];
+ defaultModified = defaultModified || arg->defaultValueExpression() != arg->originalDefaultValueExpression();
+ if (defaultModified) {
+ if (arg->defaultValueExpression().isEmpty())
+ badModifications = true;
+ else
+ otherArgs.prepend(arg->defaultValueExpression());
+ }
+ }
+
+ userArgs += otherArgs;
+ }
+
+ if (badModifications) {
+ // When an argument is removed from a method signature and no other
+ // means of calling the method is provided the generator must write
+ // a compiler error line stating the situation.
+ s << INDENT << "#error No way to call \"" << func->ownerClass()->name();
+ s << "::" << func->minimalSignature();
+ s << "\" with the modifications provided on typesystem file" << endl;
+ } else if (func->isOperatorOverload()) {
+ QString firstArg("cpp_arg0");
+ QString secondArg("cpp_arg0");
+ QString selfArg = QString("(*%1)").arg(cpythonWrapperCPtr(func->ownerClass()));
+
+ if (ShibokenGenerator::isReverseOperator(func) || func->isUnaryOperator())
+ secondArg = selfArg;
+ else
+ firstArg = selfArg;
+
+ QString op = func->originalName();
+ op = op.right(op.size() - QString("operator").size());
+
+ s << INDENT;
+ if (!func->isInplaceOperator())
+ s << retvalVariableName() << " = ";
+ if (func->isBinaryOperator())
+ s << firstArg << ' ';
+ s << op << ' ' << secondArg << ';';// << endl;
+ s << "// " << func->minimalSignature() << func->isOperatorOverload() << func->isReverseOperator() << func->isUnaryOperator() << endl;
+ } else {
+ s << INDENT;
+ if (func->isConstructor() || func->isCopyConstructor()) {
+ s << "cptr = new " << wrapperName(func->ownerClass());
+ } else {
+ if (func->type())
+ s << retvalVariableName() << " = ";
+ if (func->ownerClass()) {
+ if (!func->isStatic())
+ s << cpythonWrapperCPtr(func->ownerClass()) << "->";
+ s << func->ownerClass()->name() << "::";
+ }
+ s << func->originalName();
+ }
+
+ s << '(';
+ if (func->isCopyConstructor())
+ s << "*((" << func->ownerClass()->qualifiedCppName() << "*)cpp_arg0)";
+ else
+ s << userArgs.join(", ");
+ s << ");" << endl;
+ }
+ }
+
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func);
+}
+
+void CppGenerator::writeClassDefinition(QTextStream& s, const AbstractMetaClass* metaClass)
+{
+ QString tp_flags;
+ QString tp_new;
+ QString tp_dealloc;
+ QString tp_as_number = QString('0');
+ QString tp_richcompare = QString('0');
+ QString className = metaClass->qualifiedCppName();
+ QString baseClassName;
+
+ if (metaClass->hasArithmeticOperatorOverload()
+ || metaClass->hasLogicalOperatorOverload()
+ || metaClass->hasBitwiseOperatorOverload()) {
+ tp_as_number = QString("&Py%1_as_number").arg(metaClass->qualifiedCppName());
+ }
+
+ if (metaClass->baseClass())
+ baseClassName = QString("&") + cpythonTypeName(metaClass->baseClass()->typeEntry());
+ else
+ baseClassName = QString("0");
+
+ if (metaClass->isNamespace()) {
+ tp_flags = QString("Py_TPFLAGS_HAVE_CLASS");
+ tp_new = QString("0");
+ tp_dealloc = QString("0");
+ } else {
+ tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE";
+ tp_new = QString("(newfunc)Py%1_New").arg(className);
+ tp_dealloc = QString("(destructor)&(Shiboken::PyBaseWrapper_Dealloc< %1 >)").arg(className);
+ if (metaClass->hasComparisonOperatorOverload())
+ tp_richcompare = QString("(richcmpfunc)Py%1_richcompare").arg(className);
+ }
+
+ s << "// Class Definition -----------------------------------------------" << endl;
+
+ s << "PyTypeObject Py" << className << "_Type = {" << endl;
+ s << INDENT << "PyObject_HEAD_INIT(&PyType_Type)" << endl;
+ s << INDENT << "/*ob_size*/ 0," << endl;
+ s << INDENT << "/*tp_name*/ const_cast<char*>(\"" << className << "\")," << endl;
+ s << INDENT << "/*tp_basicsize*/ sizeof(Shiboken::PyBaseWrapper)," << endl;
+ s << INDENT << "/*tp_itemsize*/ 0," << endl;
+ s << INDENT << "/*tp_dealloc*/ " << tp_dealloc << ',' << endl;
+ s << INDENT << "/*tp_print*/ 0," << endl;
+ s << INDENT << "/*tp_getattr*/ 0," << endl;
+ s << INDENT << "/*tp_setattr*/ 0," << endl;
+ s << INDENT << "/*tp_compare*/ 0," << endl;
+ s << INDENT << "/*tp_repr*/ 0," << endl;
+ s << INDENT << "/*tp_as_number*/ " << tp_as_number << ',' << endl;
+ s << INDENT << "/*tp_as_sequence*/ 0," << endl;
+ s << INDENT << "/*tp_as_mapping*/ 0," << endl;
+ s << INDENT << "/*tp_hash*/ 0," << endl;
+ s << INDENT << "/*tp_call*/ 0," << endl;
+ s << INDENT << "/*tp_str*/ 0," << endl;
+ s << INDENT << "/*tp_getattro*/ 0," << endl;
+ s << INDENT << "/*tp_setattro*/ 0," << endl;
+ s << INDENT << "/*tp_as_buffer*/ 0," << endl;
+ s << INDENT << "/*tp_flags*/ " << tp_flags << ',' << endl;
+ s << INDENT << "/*tp_doc*/ 0," << endl;
+ s << INDENT << "/*tp_traverse*/ 0," << endl;
+ s << INDENT << "/*tp_clear*/ 0," << endl;
+ s << INDENT << "/*tp_richcompare*/ " << tp_richcompare << ',' << endl;
+ s << INDENT << "/*tp_weaklistoffset*/ 0," << endl;
+ s << INDENT << "/*tp_iter*/ 0," << endl;
+ s << INDENT << "/*tp_iternext*/ 0," << endl;
+ s << INDENT << "/*tp_methods*/ Py" << className << "_methods," << endl;
+ s << INDENT << "/*tp_members*/ 0," << endl;
+ s << INDENT << "/*tp_getset*/ 0," << endl;
+ s << INDENT << "/*tp_base*/ " << baseClassName << ',' << endl;
+ s << INDENT << "/*tp_dict*/ 0," << endl;
+ s << INDENT << "/*tp_descr_get*/ 0," << endl;
+ s << INDENT << "/*tp_descr_set*/ 0," << endl;
+ s << INDENT << "/*tp_dictoffset*/ 0," << endl;
+ s << INDENT << "/*tp_init*/ 0," << endl;
+ s << INDENT << "/*tp_alloc*/ PyType_GenericAlloc," << endl;
+ s << INDENT << "/*tp_new*/ " << tp_new << ',' << endl;
+ s << INDENT << "/*tp_free*/ PyObject_Del," << endl;
+ s << INDENT << "/*tp_is_gc*/ 0," << endl;
+ s << INDENT << "/*tp_bases*/ 0," << endl;
+ s << INDENT << "/*tp_mro*/ 0," << endl;
+ s << INDENT << "/*tp_cache*/ 0," << endl;
+ s << INDENT << "/*tp_subclasses*/ 0," << endl;
+ s << INDENT << "/*tp_weaklist*/ 0" << endl;
+ s << "};" << endl << endl;
+}
+
+void CppGenerator::writeTypeAsNumberDefinition(QTextStream& s, const AbstractMetaClass* metaClass)
+{
+ QMap<QString, QString> nb;
+
+ nb["__add__"] = QString('0');
+ nb["__sub__"] = QString('0');
+ nb["__mul__"] = QString('0');
+ nb["__div__"] = QString('0');
+ nb["__mod__"] = QString('0');
+ nb["__neg__"] = QString('0');
+ nb["__pos__"] = QString('0');
+ nb["__invert__"] = QString('0');
+ nb["__lshift__"] = QString('0');
+ nb["__rshift__"] = QString('0');
+ nb["__and__"] = QString('0');
+ nb["__xor__"] = QString('0');
+ nb["__or__"] = QString('0');
+ nb["__iadd__"] = QString('0');
+ nb["__isub__"] = QString('0');
+ nb["__imul__"] = QString('0');
+ nb["__idiv__"] = QString('0');
+ nb["__imod__"] = QString('0');
+ nb["__ilshift__"] = QString('0');
+ nb["__irshift__"] = QString('0');
+ nb["__iand__"] = QString('0');
+ nb["__ixor__"] = QString('0');
+ nb["__ior__"] = QString('0');
+
+ QList<AbstractMetaFunctionList> opOverloads =
+ filterGroupedOperatorFunctions(metaClass,
+ AbstractMetaClass::ArithmeticOp
+ | AbstractMetaClass::LogicalOp
+ | AbstractMetaClass::BitwiseOp);
+
+ foreach (AbstractMetaFunctionList opOverload, opOverloads) {
+ const AbstractMetaFunction* rfunc = opOverload[0];
+ QString opName = ShibokenGenerator::pythonOperatorFunctionName(rfunc->originalName());
+ nb[opName] = cpythonFunctionName(rfunc);
+ }
+
+ s << "static PyNumberMethods Py" << metaClass->qualifiedCppName();
+ s << "_as_number = {" << endl;
+ s << INDENT << "/*nb_add*/ (binaryfunc)" << nb["__add__"] << ',' << endl;
+ s << INDENT << "/*nb_subtract*/ (binaryfunc)" << nb["__sub__"] << ',' << endl;
+ s << INDENT << "/*nb_multiply*/ (binaryfunc)" << nb["__mul__"] << ',' << endl;
+ s << INDENT << "/*nb_divide*/ (binaryfunc)" << nb["__div__"] << ',' << endl;
+ s << INDENT << "/*nb_remainder*/ (binaryfunc)" << nb["__mod__"] << ',' << endl;
+ s << INDENT << "/*nb_divmod*/ 0," << endl;
+ s << INDENT << "/*nb_power*/ 0," << endl;
+ s << INDENT << "/*nb_negative*/ (unaryfunc)" << nb["__neg__"] << ',' << endl;
+ s << INDENT << "/*nb_positive*/ (unaryfunc)" << nb["__pos__"] << ',' << endl;
+ s << INDENT << "/*nb_absolute*/ 0," << endl;
+ s << INDENT << "/*nb_nonzero*/ 0," << endl;
+ s << INDENT << "/*nb_invert*/ (unaryfunc)" << nb["__invert__"] << ',' << endl;
+ s << INDENT << "/*nb_lshift*/ (binaryfunc)" << nb["__lshift__"] << ',' << endl;
+ s << INDENT << "/*nb_rshift*/ (binaryfunc)" << nb["__rshift__"] << ',' << endl;
+ s << INDENT << "/*nb_and*/ (binaryfunc)" << nb["__and__"] << ',' << endl;
+ s << INDENT << "/*nb_xor*/ (binaryfunc)" << nb["__xor__"] << ',' << endl;
+ s << INDENT << "/*nb_or*/ (binaryfunc)" << nb["__or__"] << ',' << endl;
+ s << INDENT << "/*nb_coerce*/ 0," << endl;
+ s << INDENT << "/*nb_int*/ 0," << endl;
+ s << INDENT << "/*nb_long*/ 0," << endl;
+ s << INDENT << "/*nb_float*/ 0," << endl;
+ s << INDENT << "/*nb_oct*/ 0," << endl;
+ s << INDENT << "/*nb_hex*/ 0," << endl;
+ s << INDENT << "/*nb_inplace_add*/ (binaryfunc)" << nb["__iadd__"] << ',' << endl;
+ s << INDENT << "/*nb_inplace_subtract*/ (binaryfunc)" << nb["__isub__"] << ',' << endl;
+ s << INDENT << "/*nb_inplace_multiply*/ (binaryfunc)" << nb["__imul__"] << ',' << endl;
+ s << INDENT << "/*nb_inplace_divide*/ (binaryfunc)" << nb["__idiv__"] << ',' << endl;
+ s << INDENT << "/*nb_inplace_remainder*/ (binaryfunc)" << nb["__imod__"] << ',' << endl;
+ s << INDENT << "/*nb_inplace_power*/ 0," << endl;
+ s << INDENT << "/*nb_inplace_lshift*/ (binaryfunc)" << nb["__ilshift__"] << ',' << endl;
+ s << INDENT << "/*nb_inplace_rshift*/ (binaryfunc)" << nb["__irshift__"] << ',' << endl;
+ s << INDENT << "/*nb_inplace_and*/ (binaryfunc)" << nb["__iand__"] << ',' << endl;
+ s << INDENT << "/*nb_inplace_xor*/ (binaryfunc)" << nb["__ixor__"] << ',' << endl;
+ s << INDENT << "/*nb_inplace_or*/ (binaryfunc)" << nb["__ior__"] << ',' << endl;
+ s << INDENT << "/*nb_floor_divide*/ 0," << endl;
+ s << INDENT << "/*nb_true_divide*/ 0," << endl;
+ s << INDENT << "/*nb_inplace_floor_divide*/ 0," << endl;
+ s << INDENT << "/*nb_inplace_true_divide*/ 0," << endl;
+ s << INDENT << "/*nb_index*/ 0" << endl;
+ s << "};" << endl << endl;
+}
+
+void CppGenerator::writeRichCompareFunction(QTextStream& s, const AbstractMetaClass* metaClass)
+{
+ s << "static PyObject*" << endl;
+ s << "Py" << metaClass->qualifiedCppName();
+ s << "_richcompare(PyObject* self, PyObject* other, int op)" << endl;
+ s << '{' << endl;
+
+ QList<AbstractMetaFunctionList> cmpOverloads = filterGroupedOperatorFunctions(metaClass, AbstractMetaClass::ComparisonOp);
+
+ s << INDENT << "bool result;" << endl;
+
+ QString arg0TypeName = metaClass->qualifiedCppName();
+ s << INDENT << arg0TypeName << "& cpp_self = *" << cpythonWrapperCPtr(metaClass) << ';' << endl;
+ s << endl;
+
+ s << INDENT << "switch (op) {" << endl;
+ {
+ Indentation indent(INDENT);
+ foreach (AbstractMetaFunctionList overloads, cmpOverloads) {
+ PolymorphicData polyData(overloads);
+ const AbstractMetaFunction* rfunc = overloads[0];
+
+ // DEBUG
+ // QString dumpFile = QString("%1_%2.dot").arg(rfunc->ownerClass()->name()).arg(pythonOperatorFunctionName(rfunc)).toLower();
+ // polyData.dumpGraph(dumpFile);
+ // DEBUG
+
+ s << INDENT << "case " << ShibokenGenerator::pythonRichCompareOperatorId(rfunc) << ':' << endl;
+
+ Indentation indent(INDENT);
+
+ QString op = rfunc->originalName();
+ op = op.right(op.size() - QString("operator").size());
+
+ int alternativeNumericTypes = 0;
+ foreach (const AbstractMetaFunction* func, overloads) {
+ if (!func->isStatic() &&
+ ShibokenGenerator::isNumber(func->arguments()[0]->type()->typeEntry()))
+ alternativeNumericTypes++;
+ }
+
+ bool first = true;
+ foreach (const AbstractMetaFunction* func, overloads) {
+ if (func->isStatic())
+ continue;
+
+ const AbstractMetaType* type = func->arguments()[0]->type();
+ bool numberType = alternativeNumericTypes == 1 || ShibokenGenerator::isPyInt(type);
+
+ if (!first) {
+ s << " else ";
+ } else {
+ first = false;
+ s << INDENT;
+ }
+
+ s << "if (" << cpythonCheckFunction(type, numberType) << "(other)) {" << endl;
+ {
+ Indentation indent(INDENT);
+ s << INDENT;
+ if (type->isValue() || type->isObject()) {
+ s << arg0TypeName << "& cpp_other = *";
+ s << cpythonWrapperCPtr(metaClass, "other");
+ } else {
+ s << translateType(type, metaClass) << " cpp_other = ";
+ writeToCppConversion(s, type, metaClass, "other");
+ }
+ s << ';' << endl;
+ s << INDENT << "result = (cpp_self " << op << " cpp_other);" << endl;
+ }
+ s << INDENT << '}';
+ }
+ s << " else goto Py" << metaClass->name() << "_RichComparison_TypeError;" << endl;
+ s << endl;
+
+ s << INDENT << "break;" << endl;
+ }
+ s << INDENT << "default:" << endl;
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");" << endl;
+ s << INDENT << "return 0;" << endl;
+ }
+ }
+ s << INDENT << '}' << endl << endl;
+
+ s << INDENT << "if (result)" << endl;
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "Py_RETURN_TRUE;" << endl;
+ }
+ s << INDENT << "Py_RETURN_FALSE;" << endl << endl;
+
+ s << INDENT << "Py" << metaClass->name() << "_RichComparison_TypeError:" << endl;
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyErr_SetString(PyExc_TypeError, \"operator called with wrong parameters.\");" << endl;
+ s << INDENT << "return 0;" << endl;
+ }
+ s << '}' << endl << endl;
+}
+
+void CppGenerator::writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList overloads)
+{
+ QPair<int, int> minMax = PolymorphicData::getMinMaxArguments(overloads);
+ const AbstractMetaFunction* func = overloads[0];
+
+ s << INDENT << "{const_cast<char*>(\"" << func->name() << "\"), (PyCFunction)";
+ s << cpythonFunctionName(func) << ", ";
+
+ if (minMax.second < 2) {
+ if (minMax.first == 0)
+ s << "METH_NOARGS";
+ if (minMax.first != minMax.second)
+ s << '|';
+ if (minMax.second == 1)
+ s << "METH_O";
+ } else {
+ s << "METH_VARARGS";
+ }
+ if (func->ownerClass() && func->isStatic())
+ s << "|METH_STATIC";
+ s << "}," << endl;
+}
+
+void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnum* cppEnum)
+{
+ QString cpythonName = cpythonEnumName(cppEnum);
+ QString addFunction;
+ if (cppEnum->enclosingClass()) {
+ addFunction = QString("PyDict_SetItemString(Py")
+ + cppEnum->enclosingClass()->name()
+ + "_Type.tp_dict,";
+ } else {
+ addFunction = "PyModule_AddObject(module,";
+ }
+
+ s << INDENT << "// init enum class: " << cppEnum->name() << endl;
+
+ s << INDENT << "if (PyType_Ready(&" << cpythonName << "_Type) < 0)" << endl;
+ s << INDENT << INDENT << "return;" << endl;
+
+ s << INDENT << "Py_INCREF(&" << cpythonName << "_Type);" << endl;
+
+ s << INDENT << addFunction << endl;
+ s << INDENT << INDENT << INDENT << "const_cast<char*>(\"";
+ s << cppEnum->name() << "\"), ((PyObject*)&" << cpythonName;
+ s << "_Type));" << endl;
+
+ foreach (const AbstractMetaEnumValue* enumValue, cppEnum->values()) {
+ if (cppEnum->typeEntry()->isEnumValueRejected(enumValue->name()))
+ continue;
+
+ s << INDENT << "enum_item = Shiboken::PyEnumObject_New(&";
+ s << cpythonName << "_Type," << endl;
+ s << INDENT << INDENT << INDENT << "const_cast<char*>(\"";
+ s << enumValue->name() << "\"), (long) ";
+ if (cppEnum->enclosingClass())
+ s << cppEnum->enclosingClass()->qualifiedCppName() << "::";
+ s << enumValue->name() << ");" << endl;
+
+ s << INDENT << addFunction << endl;
+ s << INDENT << INDENT << INDENT << "const_cast<char*>(\"";
+ s << enumValue->name() << "\"), enum_item);" << endl;
+ }
+ s << endl;
+}
+
+void CppGenerator::writeEnumNewMethod(QTextStream& s, const AbstractMetaEnum* cppEnum)
+{
+ QString cpythonName = cpythonEnumName(cppEnum);
+
+ s << "static PyObject*" << endl;
+ s << cpythonName << "_New(PyTypeObject *type, PyObject *args, PyObject *kwds)" << endl;
+ s << '{' << endl;
+
+ s << INDENT << "if (!PyType_IsSubtype(type, &" << cpythonName << "_Type))" << endl;
+ s << INDENT << INDENT << "return 0;" << endl << endl;
+
+ s << INDENT << "PyStringObject* item_name;" << endl;
+ s << INDENT << "int item_value;" << endl;
+
+ s << INDENT << "if (!PyArg_ParseTuple(args, \"Si:__new__\", &item_name, &item_value))" << endl;
+ s << INDENT << INDENT << "return 0;" << endl << endl;
+
+ s << INDENT << "PyObject* self = Shiboken::PyEnumObject_New";
+ s << "(type, (PyObject*) item_name, item_value);" << endl;
+
+ s << endl << INDENT << "if (!self)" << endl << INDENT << INDENT << "return 0;" << endl;
+
+ s << INDENT << "return self;" << endl << '}' << endl;
+}
+
+void CppGenerator::writeEnumDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum)
+{
+ QString cpythonName = cpythonEnumName(cppEnum);
+ QString newFunc;
+
+ if (cppEnum->typeEntry()->isExtensible()) {
+ newFunc = QString("(newfunc)") + cpythonName + "_New";
+ writeEnumNewMethod(s, cppEnum);
+ s << endl;
+ } else {
+ newFunc = "Shiboken::PyEnumObject_NonExtensibleNew";
+ }
+
+ s << "static PyGetSetDef " << cpythonName << "_getsetlist[] = {" << endl;
+ s << INDENT << "{const_cast<char *>(\"name\"), (getter)Shiboken::PyEnumObject_name}," << endl;
+ s << INDENT << "{0} // Sentinel" << endl;
+ s << "};" << endl << endl;
+
+ s << "PyTypeObject " << cpythonName << "_Type = {" << endl;
+ s << INDENT << "PyObject_HEAD_INIT(&PyType_Type)" << endl;
+ s << INDENT << "/*ob_size*/ 0," << endl;
+ s << INDENT << "/*tp_name*/ const_cast<char*>(\"" << cppEnum->name() << "\")," << endl;
+ s << INDENT << "/*tp_basicsize*/ sizeof(Shiboken::PyEnumObject)," << endl;
+ s << INDENT << "/*tp_itemsize*/ 0," << endl;
+ s << INDENT << "/*tp_dealloc*/ 0," << endl;
+ s << INDENT << "/*tp_print*/ 0," << endl;
+ s << INDENT << "/*tp_getattr*/ 0," << endl;
+ s << INDENT << "/*tp_setattr*/ 0," << endl;
+ s << INDENT << "/*tp_compare*/ 0," << endl;
+ s << INDENT << "/*tp_repr*/ Shiboken::PyEnumObject_repr," << endl;
+ s << INDENT << "/*tp_as_number*/ 0," << endl;
+ s << INDENT << "/*tp_as_sequence*/ 0," << endl;
+ s << INDENT << "/*tp_as_mapping*/ 0," << endl;
+ s << INDENT << "/*tp_hash*/ 0," << endl;
+ s << INDENT << "/*tp_call*/ 0," << endl;
+ s << INDENT << "/*tp_str*/ Shiboken::PyEnumObject_repr," << endl;
+ s << INDENT << "/*tp_getattro*/ 0," << endl;
+ s << INDENT << "/*tp_setattro*/ 0," << endl;
+ s << INDENT << "/*tp_as_buffer*/ 0," << endl;
+ s << INDENT << "/*tp_flags*/ Py_TPFLAGS_DEFAULT," << endl;
+ s << INDENT << "/*tp_doc*/ 0," << endl;
+ s << INDENT << "/*tp_traverse*/ 0," << endl;
+ s << INDENT << "/*tp_clear*/ 0," << endl;
+ s << INDENT << "/*tp_richcompare*/ 0," << endl;
+ s << INDENT << "/*tp_weaklistoffset*/ 0," << endl;
+ s << INDENT << "/*tp_iter*/ 0," << endl;
+ s << INDENT << "/*tp_iternext*/ 0," << endl;
+ s << INDENT << "/*tp_methods*/ 0," << endl;
+ s << INDENT << "/*tp_members*/ 0," << endl;
+ s << INDENT << "/*tp_getset*/ " << cpythonName << "_getsetlist," << endl;
+ s << INDENT << "/*tp_base*/ &PyInt_Type," << endl;
+ s << INDENT << "/*tp_dict*/ 0," << endl;
+ s << INDENT << "/*tp_descr_get*/ 0," << endl;
+ s << INDENT << "/*tp_descr_set*/ 0," << endl;
+ s << INDENT << "/*tp_dictoffset*/ 0," << endl;
+ s << INDENT << "/*tp_init*/ 0," << endl;
+ s << INDENT << "/*tp_alloc*/ 0," << endl;
+ s << INDENT << "/*tp_new*/ " << newFunc << ',' << endl;
+ s << INDENT << "/*tp_free*/ 0," << endl;
+ s << INDENT << "/*tp_is_gc*/ 0," << endl;
+ s << INDENT << "/*tp_bases*/ 0," << endl;
+ s << INDENT << "/*tp_mro*/ 0," << endl;
+ s << INDENT << "/*tp_cache*/ 0," << endl;
+ s << INDENT << "/*tp_subclasses*/ 0," << endl;
+ s << INDENT << "/*tp_weaklist*/ 0" << endl;
+ s << "};" << endl << endl;
+}
+
+void CppGenerator::writeClassRegister(QTextStream& s, const AbstractMetaClass* metaClass)
+{
+ QString pyTypeName = "Py" + metaClass->name() + "_Type";
+ s << "PyAPI_FUNC(void)" << endl;
+ s << "init_" << metaClass->name().toLower() << "(PyObject *module)" << endl;
+ s << '{' << endl;
+ s << INDENT << "if (PyType_Ready(&" << pyTypeName << ") < 0)" << endl;
+ s << INDENT << INDENT << "return;" << endl << endl;
+ s << INDENT << "Py_INCREF(&" << pyTypeName << ");" << endl;
+ s << INDENT << "PyModule_AddObject(module, const_cast<char*>(\"";
+ s << metaClass->name() << "\")," << endl;
+ s << INDENT << INDENT << "((PyObject*)&" << pyTypeName << "));" << endl << endl;
+
+ if (!metaClass->enums().isEmpty()) {
+ s << INDENT << "// Initialize enums" << endl;
+ s << INDENT << "PyObject* enum_item;" << endl << endl;
+ }
+ foreach (const AbstractMetaEnum* cppEnum, metaClass->enums())
+ writeEnumInitialization(s, cppEnum);
+
+ s << '}' << endl << endl;
+}
+
+void CppGenerator::finishGeneration()
+{
+ //Generate CPython wrapper file
+ QString classInitDecl;
+ QTextStream s_classInitDecl(&classInitDecl);
+ QString classPythonDefines;
+ QTextStream s_classPythonDefines(&classPythonDefines);
+ QString namespaceDefines;
+ QTextStream s_namespaceDefines(&namespaceDefines);
+
+ QSet<QString> includes;
+ QString globalFunctionImpl;
+ QTextStream s_globalFunctionImpl(&globalFunctionImpl);
+ QString globalFunctionDecl;
+ QTextStream s_globalFunctionDef(&globalFunctionDecl);
+
+ Indentation indent(INDENT);
+
+ foreach (AbstractMetaFunctionList globalOverloads, filterGroupedFunctions()) {
+ AbstractMetaFunctionList overloads;
+ foreach (AbstractMetaFunction* func, globalOverloads) {
+ if (!func->isModifiedRemoved())
+ overloads.append(func);
+ }
+
+ if (overloads.isEmpty())
+ continue;
+
+ writeMethodWrapper(s_globalFunctionImpl, overloads);
+ writeMethodDefinition(s_globalFunctionDef, overloads);
+ }
+
+ foreach (const AbstractMetaClass *cls, classes()) {
+ if (!shouldGenerate(cls) || cls->enclosingClass())
+ continue;
+
+ if (m_packageName.isEmpty())
+ m_packageName = cls->package();
+
+ s_classInitDecl << "extern \"C\" PyAPI_FUNC(void) init_"
+ << cls->name().toLower() << "(PyObject* module);" << endl;
+
+ QString defineStr = "init_" + cls->name().toLower() + "(module);";
+ if (cls->isNamespace())
+ s_namespaceDefines << INDENT << defineStr << endl;
+ else
+ s_classPythonDefines << INDENT << defineStr << endl;
+ }
+
+ QString moduleFileName(outputDirectory() + "/" + subDirectoryForPackage(m_packageName));
+ moduleFileName += "/" + moduleName().toLower() + "_module_wrapper.cpp";
+
+ QFile file(moduleFileName);
+ if (file.open(QFile::WriteOnly)) {
+ QTextStream s(&file);
+
+ // write license comment
+ s << licenseComment() << endl;
+
+ s << "#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */" << endl;
+ s << "#define PyMODINIT_FUNC void" << endl << "#endif" << endl << endl;
+
+ s << "#include <Python.h>" << endl;
+ s << "#include <shiboken.h>" << endl;
+ s << "#include \"" << moduleName().toLower() << "_python.h\"" << endl << endl;
+ foreach (const QString& include, includes)
+ s << "#include <" << include << ">" << endl;
+ s << endl;
+
+ s << "// Global functions ";
+ s << "------------------------------------------------------------" << endl;
+ s << globalFunctionImpl << endl;
+
+ s << "static PyMethodDef " << moduleName() << "_methods[] = {" << endl;
+ s << globalFunctionDecl;
+ s << INDENT << "{0} // Sentinel" << endl << "};" << endl << endl;
+
+ s << "// Classes initialization functions ";
+ s << "------------------------------------------------------------" << endl;
+ s << classInitDecl << endl;
+
+ if (!globalEnums().isEmpty()) {
+ s << "// Enum definitions ";
+ s << "------------------------------------------------------------" << endl;
+ foreach (const AbstractMetaEnum* cppEnum, globalEnums()) {
+ writeEnumDefinition(s, cppEnum);
+ s << endl;
+ }
+ }
+
+ s << "// Module initialization ";
+ s << "------------------------------------------------------------" << endl;
+ s << "extern \"C\" {" << endl << endl;
+
+ s << "PyMODINIT_FUNC" << endl << "init" << moduleName() << "()" << endl;
+ s << '{' << endl;
+
+ foreach (const QString& requiredModule, TypeDatabase::instance()->requiredTargetImports()) {
+ s << INDENT << "if (PyImport_ImportModule(\"" << requiredModule << "\") == NULL) {" << endl;
+ s << INDENT << INDENT << "PyErr_SetString(PyExc_ImportError," << "\"could not import ";
+ s << requiredModule << "\");" << endl << INDENT << INDENT << "return;" << endl;
+ s << INDENT << "}" << endl << endl;
+ }
+
+ s << INDENT << "PyObject* module = Py_InitModule(\"" << moduleName() << "\", ";
+ s << moduleName() << "_methods);" << endl << endl;
+
+ s << INDENT << "// Initialize classes in the type system" << endl;
+ s << classPythonDefines << endl;
+
+ s << INDENT << "// Initialize namespaces as uninstantiable classes in the type system" << endl;
+ s << namespaceDefines << endl;
+
+ if (!globalEnums().isEmpty()) {
+ s << INDENT << "// Initialize enums" << endl;
+ s << INDENT << "PyObject* enum_item;" << endl << endl;
+ }
+
+ foreach (const AbstractMetaEnum* cppEnum, globalEnums())
+ writeEnumInitialization(s, cppEnum);
+
+ s << INDENT << "if (PyErr_Occurred())" << endl;
+ s << INDENT << INDENT << "Py_FatalError(\"can't initialize module ";
+ s << moduleName() << "\");" << endl << '}' << endl << endl;
+ s << "} // extern \"C\"" << endl << endl;
+ }
+}