diff options
Diffstat (limited to 'cppgenerator.cpp')
-rw-r--r-- | cppgenerator.cpp | 1453 |
1 files changed, 1453 insertions, 0 deletions
diff --git a/cppgenerator.cpp b/cppgenerator.cpp new file mode 100644 index 000000000..a0de62bbe --- /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; + } +} |