diff options
author | Marcelo Lira <marcelo.lira@openbossa.org> | 2009-08-17 19:31:37 -0300 |
---|---|---|
committer | Marcelo Lira <marcelo.lira@openbossa.org> | 2009-08-17 19:31:37 -0300 |
commit | e0c29962e6f334452f0c9db2caaf6ed18065de85 (patch) | |
tree | cee27801c196fbcacf6130ad64216af133b555dd /headergenerator.cpp |
The End Is the Beginning Is the End
Diffstat (limited to 'headergenerator.cpp')
-rw-r--r-- | headergenerator.cpp | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/headergenerator.cpp b/headergenerator.cpp new file mode 100644 index 000000000..fe4de388b --- /dev/null +++ b/headergenerator.cpp @@ -0,0 +1,412 @@ +/* + * 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 "headergenerator.h" +#include <apiextractor/reporthandler.h> + +#include <QtCore/QDir> +#include <QtCore/QTextStream> +#include <QtCore/QVariant> +#include <QtCore/QRegExp> +#include <QtCore/QDebug> + +static Indentor INDENT; + +QString HeaderGenerator::fileNameForClass(const AbstractMetaClass* metaClass) const +{ + return metaClass->qualifiedCppName().toLower() + QLatin1String("_wrapper.h"); +} + +void HeaderGenerator::writeCopyCtor(QTextStream& s, const AbstractMetaClass* metaClass) const +{ + s << INDENT << wrapperName(metaClass) << "(const " << metaClass->qualifiedCppName() << "& self)"; + s << " : " << metaClass->qualifiedCppName() << "(self)" << endl; + s << INDENT << "{" << endl; + s << INDENT << "}" << endl << endl; +} + +void HeaderGenerator::generateClass(QTextStream& s, const AbstractMetaClass* metaClass) +{ + ReportHandler::debugSparse("Generating header for " + metaClass->fullName()); + Indentation indent(INDENT); + + // write license comment + s << licenseComment(); + + QString wrapperName = HeaderGenerator::wrapperName(metaClass); + + // Header + s << "#ifndef " << wrapperName.toUpper() << "_H" << endl; + s << "#define " << wrapperName.toUpper() << "_H" << endl<< endl; + + if (!metaClass->isNamespace()) { + s << "// The mother of all C++ binding hacks!" << endl; + s << "#define protected public" << endl << endl; + } + + s << "#include <shiboken.h>" << endl << endl; + + //Includes + if (metaClass->typeEntry()->include().isValid()) + s << metaClass->typeEntry()->include().toString() << endl << endl; + + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), + CodeSnip::Declaration, TypeSystem::NativeCode); + + if (!metaClass->isNamespace()) { + bool createWrapper = canCreateWrapperFor(metaClass); + + /* + * BOTOWTI (Beast of The Old World to be Investigated) + // detect the held type + QString held_type = metaClass->typeEntry()->heldTypeValue(); + if (held_type.isEmpty() && createWrapper) { + held_type = "qptr"; + } + + if (!held_type.isEmpty()) { + s << "// held type forward decalration" << endl; + s << "template<typename T> class " << held_type << ';' << endl; + } + */ + + // Class + s << "class SHIBOKEN_LOCAL " << wrapperName; + if (createWrapper) + s << " : public " << metaClass->qualifiedCppName(); + + s << endl << '{' << endl << "public:" << endl; + + if (metaClass->hasCloneOperator()) + writeCopyCtor(s, metaClass); + + foreach (AbstractMetaFunction *func, filterFunctions(metaClass)) + writeFunction(s, func); + + if (createWrapper) { + //destructor + s << INDENT << "~" << wrapperName << "();" << endl; + + if (metaClass->isQObject() && (metaClass->name() != "QObject")) + s << INDENT << "using QObject::parent;" << endl; + } + + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), + CodeSnip::PrototypeInitialization, TypeSystem::NativeCode); + + s << "};" << endl << endl; + } + + s << "#endif // " << wrapperName.toUpper() << "_H" << endl << endl; +} + +void HeaderGenerator::writeFunction(QTextStream& s, const AbstractMetaFunction* func) const +{ + // pure virtual functions need a default implementation + if (func->isPrivate() || (func->isModifiedRemoved() && !func->isAbstract())) + return; + + // do not write copy ctors here. + if (func->isCopyConstructor()) + return; + + if (func->isConstructor() || func->isAbstract() || func->isVirtual()) { + s << INDENT << functionSignature(func) << ';' << endl; + + // TODO: when modified an abstract method ceases to be virtual but stays abstract + //if (func->isModifiedRemoved() && func->isAbstract()) { + //} + + // TODO: APIExtractor: strange that something that is abstract couldn't be considered virtual too. + if (func->isVirtual() && !func->isAbstract() && !func->isConstructor() && + !func->ownerClass()->hasPrivateDestructor() && + func->implementingClass() == func->ownerClass()) { + writeVirtualDispatcher(s, func); + } + } +} + +void HeaderGenerator::writeVirtualDispatcher(QTextStream& s, const AbstractMetaFunction* func) const +{ + QString returnKeyword = func->type() ? QLatin1String("return ") : QString(); + s << INDENT << "static " << signatureForDefaultVirtualMethod(func, "", "_dispatcher") << " {" << endl; + { + Indentation indentation(INDENT); + s << INDENT << returnKeyword; + if (func->isModifiedRemoved() && func->isAbstract()) { + if (func->type() + && (func->type()->isObject() + || func->type()->isQObject() + || func->type()->name() == "void")) + s << "0"; + else + s << functionReturnType(func) << "()"; + } else { + s << "self." << func->implementingClass()->qualifiedCppName() << "::"; + writeFunctionCall(s, func); + } + s << ';' << endl; + } + s << INDENT << '}' << endl; +} + +void HeaderGenerator::writeTypeCheckMacro(QTextStream& s, const TypeEntry* type) +{ + QString pyTypeName = cpythonTypeName(type); + QString checkFunction = cpythonCheckFunction(type); + s << "PyAPI_DATA(PyTypeObject) " << pyTypeName << ';' << endl; + s << "#define " << checkFunction << "(op) PyObject_TypeCheck(op, &"; + s << pyTypeName << ')' << endl; + s << "#define " << checkFunction << "Exact(op) ((op)->ob_type == &"; + s << pyTypeName << ')' << endl; +} + +void HeaderGenerator::writeTypeConverter(QTextStream& s, const TypeEntry* type) +{ + QString pyTypeName = cpythonTypeName(type); + QString checkFunction = cpythonCheckFunction(type); + QString cppName = type->name(); + if (type->isObject()) + cppName.append('*'); + + s << "template<>" << endl; + s << "struct Converter< " << cppName << " >" << endl << '{' << endl; + + s << INDENT << "static PyObject* toPython(ValueHolder< "; + s << cppName << " > cppobj)" << endl << INDENT << '{' << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyObject* pyobj;" << endl; + + if (!type->isEnum()) { + s << INDENT << "ValueHolder<void*> holder((void*) "; + if (type->isValue()) + s << "new " << cppName << "(cppobj.value)"; + else + s << "cppobj.value"; + s << ");" << endl; + } + + s << INDENT << "pyobj = "; + + if (type->isEnum()) { + s << "Shiboken::PyEnumObject_New(&" << pyTypeName << ',' << endl; + s << INDENT << INDENT << "\"ReturnedValue\", (long) cppobj.value);" << endl; + } else { + QString newWrapper = QString("Shiboken::PyBaseWrapper_New(&") + + pyTypeName + ", &" + pyTypeName + + ", holder.value);"; + if (type->isValue()) { + s << newWrapper << endl; + } else { + s << "Shiboken::Converter<void*>::toPython(holder);" << endl; + s << INDENT << "if (!pyobj)" << endl; + { + Indentation indent(INDENT); + s << INDENT << "pyobj = " << newWrapper << endl; + } + } + } + + s << INDENT << "return pyobj;" << endl; + } + s << INDENT << '}' << endl; + + s << INDENT << "static inline " << cppName << " toCpp(PyObject* pyobj)" << endl; + s << INDENT << '{' << endl; + { + Indentation indent(INDENT); + + if (type->isValue()) { + AbstractMetaFunctionList implicitConverters; + if (type->isValue()) { + const AbstractMetaClass* metaClass = classes().findClass(type->qualifiedCppName()); + if (metaClass) + implicitConverters = metaClass->implicitConversions(); + } + bool firstImplicitIf = true; + foreach (const AbstractMetaFunction* ctor, implicitConverters) { + const AbstractMetaType* argType = ctor->arguments().first()->type(); + s << INDENT; + if (firstImplicitIf) + firstImplicitIf = false; + else + s << "else "; + s << "if (" << cpythonCheckFunction(argType) << "(pyobj))" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return " << cppName; + s << "(Converter< " << argType->cppSignature() << " >::toCpp(pyobj));" << endl; + } + } + } + + s << INDENT << "return "; + if (type->isEnum()) { + s << '(' << type->qualifiedCppName() << ") ((Shiboken::PyEnumObject*)pyobj)->ob_ival"; + } else { + if (type->isValue()) + s << '*'; + s << "((" << cppName; + if (type->isValue()) + s << '*'; + s << ") ((Shiboken::PyBaseWrapper*)pyobj)->cptr)"; + } + s << ';' << endl; + } + s << INDENT << '}' << endl << "};" << endl; +} + +void HeaderGenerator::finishGeneration() +{ + // Generate the main header for this module. + // This header should be included by binding modules + // extendind on top of this one. + QString classIncludes; + QTextStream s_cin(&classIncludes); + QSet<QString> enumIncludes; + QString pythonTypeStuff; + QTextStream s_pts(&pythonTypeStuff); + QString converters; + QTextStream s_c(&converters); + + Indentation indent(INDENT); + + s_pts << endl << "// Global enums" << endl; + foreach (const AbstractMetaEnum* cppEnum, globalEnums()) { + QString incFile = cppEnum->includeFile().split(QDir::separator()).takeLast(); + if (!incFile.isEmpty() && !classIncludes.contains(QString("<%1>").arg(incFile))) + enumIncludes << incFile; + writeTypeCheckMacro(s_pts, cppEnum->typeEntry()); + s_pts << endl; + writeTypeConverter(s_c, cppEnum->typeEntry()); + s_c << endl; + } + + foreach (AbstractMetaClass* metaClass, classes()) { + const TypeEntry* classType = metaClass->typeEntry(); + if (!shouldGenerate(metaClass) || metaClass->enclosingClass() || + !(classType->isObject() || classType->isValue() || classType->isNamespace())) + continue; + + if (m_packageName.isEmpty()) + m_packageName = metaClass->package(); + + //Includes + if (metaClass->typeEntry()->include().isValid()) + s_cin << metaClass->typeEntry()->include().toString() << endl; + + foreach (AbstractMetaClass* innerClass, metaClass->innerClasses()) { + if (shouldGenerate(innerClass)) + s_cin << innerClass->typeEntry()->include().toString() << endl; + } + + foreach (const AbstractMetaEnum* cppEnum, metaClass->enums()) { + writeTypeCheckMacro(s_pts, cppEnum->typeEntry()); + s_pts << endl; + writeTypeConverter(s_c, cppEnum->typeEntry()); + s_c << endl; + } + + if (!metaClass->isNamespace()) { + s_pts << "PyAPI_FUNC(PyObject*) " << cpythonBaseName(metaClass->typeEntry()); + s_pts << "_New(PyTypeObject* type, PyObject* args, PyObject* kwds);" << endl; + writeTypeCheckMacro(s_pts, classType); + s_pts << "#define Py" << metaClass->name() << "_cptr(pyobj) (("; + s_pts << metaClass->name() << "*)PyBaseWrapper_cptr(pyobj))" << endl << endl; + writeTypeConverter(s_c, classType); + s_c << endl; + } + } + + QString moduleHeaderFileName(outputDirectory() + QDir::separator() + + subDirectoryForPackage(m_packageName)); + moduleHeaderFileName += QDir::separator() + moduleName().toLower() + "_python.h"; + + QString includeShield = moduleName().toUpper() + "_PYTHON_H"; + + QFile file(moduleHeaderFileName); + if (file.open(QFile::WriteOnly)) { + QTextStream s(&file); + + // write license comment + s << licenseComment() << endl << endl; + + s << "#ifndef " << includeShield << endl; + s << "#define " << includeShield << endl<< endl; + + s << "#include <Python.h>" << endl; + s << "#include <conversions.h>" << endl; + s << "#include <basewrapper.h>" << endl; + s << "#include <bindingmanager.h>" << endl << endl; + + s << "// Class Includes" << endl; + s << classIncludes << endl; + + if (!enumIncludes.isEmpty()) { + s << "// Enum Includes" << endl; + foreach (const QString& include, enumIncludes) + s << "#include <" << include << ">" << endl; + s << endl; + } + + if (!containerTypes().isEmpty()) { + s << "// Conversion Includes" << endl; + foreach (const ContainerTypeEntry* ctype, containerTypes()) { + if (ctype->include().isValid()) + s << ctype->include().toString() << endl; + } + s << endl; + } + + s << "extern \"C\"" << endl << '{' << endl << endl; + s << pythonTypeStuff << endl; + s << "} // extern \"C\"" << endl << endl; + + s << "namespace Shiboken" << endl << '{' << endl << endl; + + s << "// User defined converters --------------------------------------------" << endl; + + foreach (const PrimitiveTypeEntry* ptype, primitiveTypes()) { + if (!ptype->codeSnips().isEmpty()) { + foreach (CodeSnip snip, ptype->codeSnips()) + s << snip.code(); + } + } + + foreach (const ContainerTypeEntry* ctype, containerTypes()) { + if (!ctype->codeSnips().isEmpty()) { + foreach (CodeSnip snip, ctype->codeSnips()) + s << snip.code(); + } + } + + s << "// Generated converters -----------------------------------------------" << endl << endl; + + s << converters << endl; + + s << "} // namespace Shiboken" << endl << endl; + + s << "#endif // " << includeShield << endl << endl; + } +} |