diff options
Diffstat (limited to 'cppgenerator.cpp')
-rw-r--r-- | cppgenerator.cpp | 1421 |
1 files changed, 1421 insertions, 0 deletions
diff --git a/cppgenerator.cpp b/cppgenerator.cpp new file mode 100644 index 000000000..ae1f625a8 --- /dev/null +++ b/cppgenerator.cpp @@ -0,0 +1,1421 @@ +/* + * This file is part of the Boost Python 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 <apiextractor/fileout.h> +#include <apiextractor/abstractmetalang.h> + +#include <QtCore/QDir> +#include <QtCore/QTextStream> +#include <QtCore/QVariant> +#include <QtCore/QRegExp> +#include <QtCore/QDebug> +#include <QtCore/QListIterator> + +static Indentor INDENT; + +// utiliy functions +inline QString getMethodPointerString(const AbstractMetaFunction* func) +{ + QString className; + if (!func->declaringClass()->isAbstract()) + className = func->declaringClass()->qualifiedCppName(); + else + className = func->ownerClass()->qualifiedCppName(); + + return '&' + className + "::" + func->originalName(); +} + +static QString nameForModifiedCtorFunction(const AbstractMetaFunction* func) { + QString res = func->ownerClass()->name().toLower().replace("::", "_"); + res += "_constructor"; + foreach (AbstractMetaArgument* arg, func->arguments()) { + res += '_'; + res += arg->type()->name().toLower(); + } + return res; +} + +static QString createStaticFunctionName(const AbstractMetaFunction* func) +{ + QString funcName; + QString originalName(func->name()); + + + funcName = func->ownerClass()->name().toLower(); + + //remove initial 'Q' + if (funcName.startsWith('q')) + funcName = funcName.remove(0, 1); + + //upercase first letter + funcName += originalName[0].toUpper() + originalName.mid(1); + + return funcName; +} + +QString CppGenerator::fileNameForClass(const AbstractMetaClass* cppClass) const +{ + return getWrapperName(cppClass) + QLatin1String(".cpp"); +} + +QString CppGenerator::getFuncTypedefName(const AbstractMetaFunction* func) const +{ + return func->name() + QLatin1String("_type"); +} + +void CppGenerator::writeConstructorInitialization(QTextStream &s, const AbstractMetaFunction *function) +{ + QStringList nonOpts; + QStringList opts; + + foreach (AbstractMetaArgument *arg, function->arguments()) { + uint options = SkipName | SkipDefaultValues; + QString argType = argumentString(function, arg, options); + if (arg->defaultValueExpression().isEmpty()) + nonOpts << argType; + else + opts << argType; + } + + bool hasModifications = function->allowThread() || function->hasInjectedCode(); + + if (hasModifications) { + s << "\"__init__\", python::make_constructor(" + << nameForModifiedCtorFunction(function); + } else { + s << "python::init< "; + + if (nonOpts.size() > 0) + s << nonOpts.join(", "); + + if (opts.size() > 0) { + if (nonOpts.size() > 0) + s << ", "; + + s << "python::optional< " << opts.join(",") << " > "; + } + + s << " > ()"; + } + + QString callPolicy = getFunctionCallPolicy(function); + QString parentType; + const AbstractMetaClass *cppClass = function->ownerClass(); + uint closePolicy = 0; + bool hasPolicy = false; + + if ( + !hasModifications && + (!cppClass->isPolymorphic() || cppClass->hasPrivateDestructor() || cppClass->isNamespace()) + ) { + closePolicy++; + hasPolicy = true; + s << "[ PySide::register_wrapper_object< " + << function->ownerClass()->qualifiedCppName(); + } + + if (callPolicy.isEmpty()) { + int parentIndex = -1; + //try find for parent arg to create callPolicy + foreach (AbstractMetaArgument *arg, function->arguments()) { + if (arg->argumentName() == "parent") { + parentIndex = arg->argumentIndex(); + parentType = translateType(arg->type(), function->ownerClass(), + Generator::ExcludeConst | Generator::ExcludeReference).replace("*", ""); + break; + } + } + if (parentIndex != -1) { + if (!closePolicy) + s << (hasModifications ? ", " : "[ "); + else + s << ", "; + + s << "parent_policy_add< " << parentIndex + 2 << ", 1, " + << parentType << " , " << function->ownerClass()->qualifiedCppName(); + + hasPolicy = true; + closePolicy++; + } + } else { + if (!closePolicy) + s << (hasModifications ? ", " : "[ "); + else + s << ", "; + + if (callPolicy.endsWith("()")) + callPolicy = callPolicy.remove(callPolicy.size() - 2, 2); + + s << callPolicy; + hasPolicy = true; + } + + while(closePolicy) { + s << " > "; + closePolicy--; + } + + if (hasModifications) + s << ')'; + else if (hasPolicy) + s << "() ]"; +} + +QString CppGenerator::getFunctionReturnType(const AbstractMetaFunction* func) +{ + QString modifiedReturnType = QString(func->typeReplaced(0)); + + return modifiedReturnType.isNull() ? translateType(func->type(), func->implementingClass()) : modifiedReturnType; +} + +QString CppGenerator::writeFunctionCast(QTextStream &s, + const AbstractMetaFunction* func, + const QString& castNameSuffix, + const QString& className) +{ + QString castName = getFuncTypedefName(func) + castNameSuffix; + const AbstractMetaClass* cppClass = func->ownerClass(); + bool isWrapped = !func->isVirtual() && + (func->hasInjectedCode() || func->isThread() || func->allowThread()); + bool isVirtualMethodDefault = castNameSuffix == "_default"; + + s << INDENT << "typedef "; + s << getFunctionReturnType(func); + s << " ("; + if (cppClass && !func->isStatic() && func->ownerClass() && !isVirtualMethodDefault) { + if (!isWrapped) { + // pointer to a class method + if (!className.isEmpty()) + s << className; + else if (func->isVirtual() && !func->declaringClass()->isAbstract()) + s << func->declaringClass()->qualifiedCppName(); + else + s << cppClass->qualifiedCppName(); + + s << "::"; + } + } + + s << '*' << castName << ") ("; + if (isVirtualMethodDefault) { + if (func->isConstant()) + s << "const "; + + s << func->implementingClass()->qualifiedCppName() << "&"; + if (func->arguments().size() > 0) + s << ", "; + } + int options = SkipName | SkipDefaultValues | SkipRemovedArguments; + if (isWrapped && !func->isStatic()) + options |= WriteSelf; + + writeFunctionArguments(s, func, options); + s << ')'; + + if (func->isConstant() && !isWrapped && !isVirtualMethodDefault) + s << " const"; + + s << ';' << endl; + + return castName; +} + +QString CppGenerator::verifyDefaultReturnPolicy(const AbstractMetaFunction *cppFunction, const QString& callPolicy) +{ + AbstractMetaType *type = cppFunction->type(); + QString returnPolicy; + + if (type && type->isReference() && type->isConstant()) { + returnPolicy = "python::return_value_policy<python::copy_const_reference"; + if (!callPolicy.isEmpty()) + returnPolicy += ", " + callPolicy; + returnPolicy += " >()"; + } else if (type && (type->isReference() || type->isQObject() || type->isObject())) { + bool cppOwnership = type->isConstant(); + if (cppFunction->isStatic() || cppOwnership) { + returnPolicy = "python::return_value_policy<PySide::return_ptr_object< " + + (cppOwnership ? QString("true") : QString("false")) + "> >()"; + } else if (type->isQObject() || type->isObject()) { + returnPolicy = QString("PySide::return_object<1, 0, %1, %2 %3 %4 >()") + .arg(getArgumentType(cppFunction->ownerClass(), cppFunction, -1)) + .arg(getArgumentType(cppFunction->ownerClass(), cppFunction, 0)) + .arg(callPolicy.isEmpty() ? "" : ",") + .arg(callPolicy); + } else { + returnPolicy = QString("python::return_internal_reference<%1 %2>()") + .arg(callPolicy.isEmpty() ? "" : ",") + .arg(callPolicy); + } + } else if (!callPolicy.isEmpty()) { + returnPolicy = callPolicy + "()"; + } + + return returnPolicy; +} + +static int boost_parent_policy_index(int i, const AbstractMetaFunction* func = 0) +{ + if (func && func->isStatic()) + return i; + + if (i == -1) + return 1; + else if (i > 0) + return i + 1; + else + return i; +} + +QString CppGenerator::getArgumentType(const AbstractMetaClass *cppClass, const AbstractMetaFunction *func, int idx) +{ + QString retval; + if (idx == -1) { + retval = cppClass->qualifiedCppName(); + } else if (idx == 0 && func->type()) { + retval = translateType(func->type(), cppClass, + Generator::ExcludeConst | Generator::ExcludeReference); + } else if (idx > 0) { + retval = argumentString(func, func->arguments()[idx-1], + Generator::SkipDefaultValues | Generator::ExcludeConst | + Generator::ExcludeReference | Generator::SkipName); + } + + retval = retval.trimmed(); + if (retval.endsWith('*')) + retval.chop(1); + return retval; +} + +QString CppGenerator::getFunctionCallPolicy(const AbstractMetaFunction *func) +{ + QString callPolicy; + QStringList callPolicies; + bool returnChild = false; + const AbstractMetaClass* cppClass = func->implementingClass(); + + const int numArgs = func->arguments().count(); + + for (int i = -1; i <= numArgs; ++i) { + ArgumentOwner ao = func->argumentOwner(cppClass, i); + //Parent Policy + if ((ao.index != -2) && (ao.index != i)) { + switch (ao.action) { + case ArgumentOwner::Add: + if (!i) { + callPolicy = "return_object<"; + returnChild = true; + } else { + callPolicy = "parent_policy_add<"; + } + break; + case ArgumentOwner::Remove: + callPolicy = "parent_policy_remove<"; + break; + default: + continue; + } + + callPolicy += QString("%1, %2, %3, %4") + .arg(boost_parent_policy_index(ao.index, func)) + .arg(boost_parent_policy_index(i, func)) + .arg(getArgumentType(cppClass, func, ao.index)) + .arg(getArgumentType(cppClass, func, i)); + + callPolicies << callPolicy; + } else if (i) { //only function args ignore return value + //Ownership policy + bool changeOwnership = false; + bool releaseOwnership = false; + TypeSystem::Ownership owner = func->ownership(cppClass, + TypeSystem::TargetLangCode, i); + + switch(owner) + { + case TypeSystem::CppOwnership: + releaseOwnership = true; + case TypeSystem::TargetLangOwnership: + changeOwnership = true; + break; + default: + changeOwnership = false; + } + + if (changeOwnership) + { + QString ownershipPolicy = QString("transfer_ownership<%1, %2, %3") + .arg(boost_parent_policy_index(i, func)) + .arg(releaseOwnership ? "true" : "false") + .arg(getArgumentType(cppClass, func, i)); + callPolicies << ownershipPolicy; + } + } + } + + if (callPolicies.size() > 0) { + callPolicy = callPolicies.join(", "); + for (int i = 0; i < callPolicies.count(); ++i) + callPolicy += " >"; + } + + QString returnPolicy; + + //return value + bool cppOwnership = false; + + if (!returnChild) { + switch (func->ownership(cppClass, TypeSystem::TargetLangCode, 0)) + { + case TypeSystem::CppOwnership: + cppOwnership = true; + case TypeSystem::TargetLangOwnership: + { + QString cppOwnershipFlag = (cppOwnership ? "true" : "false"); + returnPolicy = "python::return_value_policy< PySide::return_ptr_object<" + cppOwnershipFlag + "> "; + if (!callPolicy.isEmpty()) + returnPolicy += ", " + callPolicy; + returnPolicy += " >()"; + break; + } + default: + returnPolicy = verifyDefaultReturnPolicy(func, callPolicy); + break; + } + } + + //return policy + if (func->shouldReturnThisObject()) + return "python::return_self< " + callPolicy + " >()"; + else if (!returnPolicy.isEmpty()) + return returnPolicy; + else if (!callPolicy.isEmpty()) + return callPolicy + "()"; + + return QString(); +} + +/*!\internal + Function used to write the enum boost code on the buffer + \param s the output buffer + \param cpp_enum the pointer to metaenum information to be translated to boost +*/ +void CppGenerator::writeEnum(QTextStream &s, + const AbstractMetaEnum *cppEnum, + const QString &nameSpace) +{ + s << INDENT << "python::enum_<" << nameSpace << cppEnum->name(); + s << ">(\"" << cppEnum->name() << "\")" << endl; + const AbstractMetaEnumValueList &values = cppEnum->values(); + EnumTypeEntry *ete = cppEnum->typeEntry(); + + foreach (const AbstractMetaEnumValue* enumValue, values) { + Indentation indent(INDENT); + if (ete->isEnumValueRejected(enumValue->name())) + continue; + + s << INDENT << ".value(\"" << enumValue->name() << "\", "; + s << nameSpace << enumValue->name() << ")" << endl; + } + + //Export values to current scope + s << INDENT << INDENT << ".export_values()" << endl; + s << INDENT << ";" << endl << endl; + + FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); + + if (flagsEntry) { + s << INDENT << "PySide::declare_" << (cppEnum->typeEntry()->forceInteger() ? "int_" : "") << "qflags< " + << flagsEntry->originalName() << " >(\"" << flagsEntry->flagsName() << "\");" << endl; + } + + //register enum in typemanager + s << INDENT + << "type_manager::instance().register_native_type<int>(\"" + << cppEnum->qualifier() << "::" << cppEnum->name() << "\");\n\n"; +} + +void CppGenerator::writeEnums(QTextStream &s, const AbstractMetaClass *cppClass, bool useNamespace) +{ + AbstractMetaEnumList enums = cppClass->enums(); + if (!enums.size()) + return; + + s << INDENT << "// Enums" << endl; + QString name_space; + if (useNamespace || !cppClass->isPolymorphic() || cppClass->hasPrivateDestructor()) + name_space = cppClass->qualifiedCppName() + "::"; + + foreach (AbstractMetaEnum *cpp_enum, enums) + writeEnum(s, cpp_enum, name_space); +} + +void CppGenerator::writeImplicitlyConversion(QTextStream &s, const AbstractMetaClass *cppClass) +{ +#if 0 + if (cppClass->isNamespace()) + return; + s << endl << "// Implicitly conversions" << endl; + QStringList interfaces = getBaseClasses(cppClass); + + if (!interfaces.size()) { + s << INDENT << "python::implicitly_convertible< " << endl; + s << INDENT << INDENT << "std::auto_ptr< " << getWrapperName(cppClass->name()) << " >," << endl; + s << INDENT << INDENT << "std::auto_ptr< " << cppClass->name() << " > >();" << endl; + } else { + foreach (QString base_class, interfaces) { + s << INDENT << "python::implicitly_convertible< " << endl; + s << INDENT << INDENT << "std::auto_ptr< " << cppClass->name() << " >," << endl; + s << INDENT << INDENT << "std::auto_ptr< " << base_class << " > >();" << endl; + } + } +#endif +} + + +void CppGenerator::writeDestructor(QTextStream &s, const AbstractMetaClass *cppClass) +{ + Indentation indentation(INDENT); + QString wrapperName = getWrapperName(cppClass); + s << wrapperName << "::~" << wrapperName << "()" << endl << "{" << endl + << INDENT << "PySide::qptr_base::invalidate(this);" << endl << "}" << endl; +} + +/*! + Function used to write the class generated boost code on the buffer + \param s the output buffer + \param cppClass the pointer to metaclass information +*/ +void CppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppClass) +{ + ReportHandler::debugSparse("Generating wrapper implementation for " + cppClass->fullName()); + + // write license comment + s << licenseComment() << endl; + + QString localStr, globalStr; + QTextStream includesLocal(&localStr); + QTextStream includesGlobal(&globalStr); + + bool canCreateWrapper = canCreateWrapperFor(cppClass); + + QList<Include> includes = cppClass->typeEntry()->extraIncludes(); + qSort(includes.begin(), includes.end()); + + foreach (Include inc, includes) { + if (inc.type == Include::IncludePath) + includesGlobal << inc.toString() << endl; + else + includesLocal << inc.toString() << endl; + } + + //workaround to access protected functions + s << "//workaround to access protected functions" << endl; + s << "#define protected public" << endl; + + s << "//Base Class" << endl; + if (cppClass->typeEntry()->include().isValid()) + s << cppClass->typeEntry()->include().toString() << endl << endl; + + s << "//Extra includes [global]" << endl; + s << globalStr << endl; + + s << "#undef protected" << endl; + s << "//Base include" << endl; + s << "#include \"pyside.hpp\"" << endl; + s << "#include \"" << getWrapperName(cppClass) << ".hpp\"" << endl; + foreach (AbstractMetaClass* innerClass, cppClass->innerClasses()) { + if (shouldGenerate(innerClass)) + s << "#include \"" << getWrapperName(innerClass) << ".hpp\"" << endl; + } + s << endl << "//Extra includes [local]" << endl; + s << localStr << endl; + + s << endl << "using namespace boost;" << endl; + s << "using namespace PySide;" << endl; + s << endl; + + if (cppClass->typeEntry()->typeFlags() & ComplexTypeEntry::Deprecated) + s << "#Deprecated" << endl; + + if (canCreateWrapper) { + writePrelude(s, cppClass); + if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor()) + writeDestructor(s, cppClass); + } + writeBoostDeclaration(s, cppClass); +} + +void CppGenerator::writePrelude(QTextStream& s, const AbstractMetaClass* cppClass) +{ + //inject code native beginner + writeCodeSnips(s, cppClass->typeEntry()->codeSnips(), + CodeSnip::Beginning, TypeSystem::NativeCode); + + foreach (AbstractMetaFunction *func, filterFunctions(cppClass)) { + if ((func->isPrivate() || func->isModifiedRemoved()) && !func->isAbstract()) + continue; + + if (func->isConstructor() && (func->allowThread() || func->hasInjectedCode())) { + writeModifiedConstructorImpl(s, func); + } else if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor() && + func->isConstructor() && !func->isCopyConstructor()) { + writeConstructorImpl(s, func); + } else if (func->isVirtual() || func->isAbstract()) { + writeVirtualMethodImpl(s, func); + } else if (func->hasInjectedCode() || func->isThread() || func->allowThread()) { + writeNonVirtualModifiedFunctionImpl(s, func); + } else if (func->isInGlobalScope() && func->isOperatorOverload()) { + writeGlobalOperatorOverloadImpl(s, func); + } + } + + //inject code native end + writeCodeSnips(s, cppClass->typeEntry()->codeSnips(), + CodeSnip::End, TypeSystem::NativeCode); +} + + +void CppGenerator::writeModifiedConstructorImpl ( QTextStream& s, const AbstractMetaFunction* func ) +{ + Indentation indentation(INDENT); + const AbstractMetaClass* clazz = func->ownerClass(); + s << "static " << clazz->name() << "* " << nameForModifiedCtorFunction(func) << '('; + writeFunctionArguments(s, func, SkipDefaultValues); + s << ")\n{" << endl; + + s << INDENT << clazz->name() << "* _self = 0;" << endl; + s << INDENT << '{' << endl; + { + Indentation indentation(INDENT); + if (func->allowThread()) + s << INDENT << "py_allow_threads allow_threads;" << endl; + + s << INDENT << "_self = new "; + writeFunctionCall(s, func); + s << ';' << endl; + } + s << INDENT << '}' << endl; + writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::All, func); + writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func); + s << INDENT << "python::object _obj(PySide::ptr(_self));" << endl; + s << INDENT << "return _self;" << endl; + s << '}' << endl; +} + +void CppGenerator::writeConstructorImpl(QTextStream& s, const AbstractMetaFunction* func) +{ + s << functionSignature(func, getWrapperName(func->ownerClass()) + "::", "", + (Option)(OriginalTypeDescription | SkipDefaultValues)); + s << " : "; + writeFunctionCall(s, func); + s << " {" << endl; + writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::All, func); + writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func); + s << '}' << endl << endl; +} + +void CppGenerator::writeVirtualMethodImplHead(QTextStream& s, const AbstractMetaFunction* func) +{ + Indentation indentation(INDENT); + s << INDENT << "thread_locker lock;" << endl; + + if (func->hasInjectedCode()) { + writeCodeSnips(s, getCodeSnips(func), + CodeSnip::Beginning, TypeSystem::NativeCode, func); + } + + s << INDENT << "python::object method = PySide::detail::get_override(this, \"" << func->implementingClass()->name(); + if (func->implementingClass()->typeEntry()->isObject() || func->implementingClass()->typeEntry()->isQObject()) + s << '*'; + + s << "\", \"" << func->name() << "\");" << endl + << INDENT << "if (method)" << endl << INDENT << "{" << endl; + + { + Indentation indentation(INDENT); + s << INDENT; + if (func->type()) + s << "python::object __result = "; + + s << "method("; + writeArgumentNames(s, func, BoxedPrimitive); + s << ");" << endl; + + QString typeName = getFunctionReturnType(func); + if (!typeName.isEmpty()) { + + CodeSnipList codeSnips = getCodeSnips(func); + bool hasVirtualBeginningCode = false; + foreach(CodeSnip cs, codeSnips) { + if ((cs.position == CodeSnip::Beginning) && (cs.language == TypeSystem::TargetLangCode)) { + hasVirtualBeginningCode = true; + break; + } + } + + if (hasVirtualBeginningCode) { + writeCodeSnips(s, codeSnips, CodeSnip::Beginning, TypeSystem::TargetLangCode, func); + } else if (func->type()) { + s << INDENT << typeName << " __return_value = " << "python::extract<" << typeName << " >(__result);" << endl; + bool boxedPointer = false; + if (func->type() && !func->type()->isConstant() && + (func->type()->isObject() || func->type()->isQObject())) { + + s << INDENT << "PySide::qptr<" << QString(typeName).replace("*", "") << " > __ptr(__result.ptr());" << endl + << INDENT << "if (__ptr.is_wrapper()) {" << endl + << INDENT << INDENT << "python::incref(__result.ptr());" << endl + << INDENT << "}" << endl + << INDENT << "__ptr.release_ownership();" << endl; + } + + s << INDENT << "return __return_value;" << endl; + } + } + } + s << INDENT << "}" << endl; +} + +void CppGenerator::writeVirtualMethodImpl(QTextStream& s, const AbstractMetaFunction* func) +{ + if (func->isModifiedRemoved()) + return; + + if (!func->isAbstract() && !func->ownerClass()->hasPrivateDestructor() && + func->implementingClass() == func->ownerClass()) { + writeVirtualDefaultFunction(s, func); + } + + + QString prefix = getWrapperName(func->ownerClass()) + "::"; + s << functionSignature(func, prefix, "", + Generator::OriginalTypeDescription | Generator::SkipDefaultValues) + << endl << "{" << endl; + + writeVirtualMethodImplHead(s, func); + + if (func->isAbstract()) + writePureVirtualMethodImplFoot(s, func); + else + writeVirtualMethodImplFoot(s, func); + + s << '}' << endl << endl; +} + + +void CppGenerator::writePureVirtualMethodImplFoot(QTextStream& s, const AbstractMetaFunction* func) +{ + Indentation indentation(INDENT); + s << INDENT << "else" << endl + << INDENT << "{" << endl; + { + Indentation indentation(INDENT); + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"" + << func->ownerClass()->name() << "." << func->name() << " : " + << "You need to implement pure virtual functions in python\");" << endl + << INDENT << "throw python::error_already_set();" << endl; + } + s << INDENT << "}" << endl; +} + +void CppGenerator::writeVirtualMethodImplFoot(QTextStream& s, const AbstractMetaFunction* func) +{ + Indentation indentation(INDENT); + s << INDENT << "else" << endl << INDENT << "{" << endl; + { + Indentation indentation(INDENT); + QString returnKeyword = func->type() ? QLatin1String("return ") : QString(); + + if (func->allowThread()) + s << INDENT << "py_allow_threads allow_threads;" << endl; + + s << INDENT << returnKeyword << func->implementingClass()->qualifiedCppName() << "::"; + writeFunctionCall(s, func); + s << ';' << endl; + } + s << INDENT << '}' << endl; +} + +void CppGenerator::writeVirtualDefaultFunction(QTextStream &s, const AbstractMetaFunction *func) +{ + Indentation indentation(INDENT); + QString returnKeyword = func->type() ? QLatin1String("return ") : QString(); + QString defaultMethodSignature = signatureForDefaultVirtualMethod(func, getWrapperName(func->ownerClass()) + "::", "_default", Generator::SkipDefaultValues); + s << defaultMethodSignature << endl << '{' << endl; + + if (func->allowThread()) + s << INDENT << "py_allow_threads allow_threads;" << endl; + + CodeSnipList codeSnips = getCodeSnips(func); + bool hasVirtualEndCode = false; + foreach(CodeSnip cs, codeSnips) { + if ((cs.position == CodeSnip::End) && (cs.language == TypeSystem::TargetLangCode)) { + hasVirtualEndCode = true; + break; + } + } + + if (!hasVirtualEndCode) { + s << INDENT << returnKeyword << "self." << func->implementingClass()->qualifiedCppName() << "::"; + writeFunctionCall(s, func); + s << ";" << endl; + } else { + writeCodeSnips(s, getCodeSnips(func), + CodeSnip::End, TypeSystem::TargetLangCode, func); + } + s << '}' << endl << endl; + +} + + + +void CppGenerator::writeNonVirtualModifiedFunctionImpl(QTextStream& s, const AbstractMetaFunction* func) +{ + Indentation indentation(INDENT); + + s << "static " << 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).size() > 0) { + 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; +} + +AbstractMetaFunction* CppGenerator::findMainConstructor(const AbstractMetaClass* clazz) +{ + foreach (AbstractMetaFunction* func, clazz->functions()) { + if (func->isConstructor() && + func->isPublic() && + !func->isModifiedRemoved() && + !func->isPrivate()) { + return func; + } + } + return 0; +} + +void CppGenerator::writeHashFunction(QTextStream& s, const AbstractMetaClass* cppClass) +{ + QString argType; + + //WORKAROUND: diferent way to QChar + if (cppClass->name() == "QChar") + argType = "QChar"; + else + argType = "const " + cppClass->name() + "&"; + + s << "// Hash function" << endl + << "{" << endl + << INDENT << INDENT << "typedef uint (*hash_type) ( " << argType << " );" + << INDENT << INDENT << "python_cls.def(\"__hash__\", hash_type(&" + << cppClass->typeEntry()->hashFunction() << "));" << endl + << "}" << endl; +} + +QString CppGenerator::baseClassName(const QString& name) +{ + QStringList lst = name.split("::"); + return lst.last(); +} + +void CppGenerator::writeBoostDeclaration(QTextStream& s, const AbstractMetaClass* cppClass) +{ + Indentation indent(INDENT); + QString wrapperName = getWrapperName(cppClass); + + s << "void " << wrapperName << "::define_python_class() throw() {" << endl; + + const AbstractMetaFunction* mainCtor = 0; + bool mainCtorHasModifications = false; + if (!cppClass->isNamespace()) { + // python_cls declaration + mainCtor = findMainConstructor(cppClass); + if (mainCtor) + mainCtorHasModifications = mainCtor->allowThread() || mainCtor->hasInjectedCode(); + + s << INDENT; + if (!cppClass->isPolymorphic() || cppClass->hasPrivateDestructor()) + s << wrapperName << "::"; + + s << "class_type python_cls(\"" + << baseClassName(cppClass->name()) << "\", "; + + if (!mainCtor || mainCtorHasModifications) + s << "python::no_init"; + else + writeConstructorInitialization(s, mainCtor); + + s << ");" << endl << endl; + } else { + QRegExp reg("(?:\\w+::)*(\\w+)"); + reg.indexIn(cppClass->name()); + s << INDENT << "python::class_<Namespace> python_cls(\"" << reg.cap(1) << "\");" << endl; + } + // scope declaration + s << INDENT << "python::scope " << wrapperName << "_scope(python_cls);" << endl; + + if (cppClass->templateBaseClass() && cppClass->templateBaseClass()->typeEntry()->isContainer()) { + //const ContainerTypeEntry *type = static_cast<const ContainerTypeEntry*>(cppClass->templateBaseClass()->typeEntry()); + //if (type->type() == ContainerTypeEntry::ListContainer) { + s << endl << INDENT << "//Index suite for QContainer" << endl + << INDENT << "python_cls.def(qcontainer_indexing_suite< " << cppClass->qualifiedCppName() << " >());" << endl << endl; + //} + } + + if (isCopyable(cppClass) && !cppClass->isNamespace()) { + s << INDENT << "python_cls.def(python::init<const "; + s << cppClass->qualifiedCppName() << "&>());" << endl; + } + + if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor() && canCreateWrapperFor(cppClass)) { + QString heldType = cppClass->typeEntry()->heldTypeValue(); + if (heldType.isEmpty()) + heldType = "PySide::qptr"; + + s << INDENT << "python::implicitly_convertible< " + << heldType << "<" << wrapperName << ">, " + << heldType << "<" << cppClass->qualifiedCppName() << "> >();" << endl; + } + + //Enums + writeEnums(s, cppClass, cppClass->hasPrivateDestructor() || cppClass->isNamespace()); + + if (cppClass->innerClasses().count()) { + s << endl << INDENT << "// Inner classes" << endl; + foreach (AbstractMetaClass* innerClass, cppClass->innerClasses()) { + if (!innerClass->typeEntry()->generateCode()) + continue; + s << INDENT << getWrapperName(innerClass) << "::define_python_class();" << endl; + } + } + + //Fields + foreach (AbstractMetaField *field, cppClass->fields()) { + QString strAccess; + + if (field->isPublic()) { + if (field->type()->isConstant()) + strAccess = "def_readonly"; + else + strAccess = "def_readwrite"; + + s << INDENT << "python_cls." + << strAccess + << "(\"" << field->name() << "\", &" + << field->enclosingClass()->name() << "::" << field->name() << ");" << endl; + } + } + + writeCodeSnips(s, cppClass->typeEntry()->codeSnips(), + CodeSnip::Beginning, TypeSystem::TargetLangCode); + + QSet<QString> staticMethods; + + if (!cppClass->isNamespace()) { + //search for all static methods to match with normal functions + //to rename when match with one member function + foreach (AbstractMetaFunction *func, filterFunctions(cppClass)) { + if (func->isStatic() && !func->isOperatorOverload()) + staticMethods << func->name(); + } + } + + foreach (AbstractMetaFunction *func, filterFunctions(cppClass)) { + if (func->isModifiedRemoved() || func->isPrivate() || func->isSignal()) + continue; + + //rename static function when is the same name as member function + if (!cppClass->isNamespace() && func->isStatic()) { + QString staticName(createStaticFunctionName(func)); + QSet<QString>::iterator staticFuncInter = staticMethods.find(staticName); + if (staticFuncInter != staticMethods.end()) + func->setName(staticName); + } else { + QSet<QString>::iterator staticFuncInter = staticMethods.find(func->name()); + if (staticFuncInter != staticMethods.end()) { + staticMethods.erase(staticFuncInter); + staticMethods << createStaticFunctionName(func); + } + } + + if (func->isOperatorOverload()) { + // Do not join the ifs -- isOperatorOverload must be checked alone + if (func->originalName() == func->name()) + writeOperatorOverload(s, func); + } else if (func->isConstructor()) { + if (mainCtorHasModifications || func != mainCtor) + writeConstructor(s, func); + } else if (!func->isVirtual() && + (func->hasInjectedCode() || + func->isThread() || func->allowThread())) { + writeModifiedMethodDef(s, func); + } else if (func->implementingClass() == func->ownerClass()) { + writeNormalMethodDef(s, func); + } + + //if is namespace all methothds is stattic + if (cppClass->isNamespace()) + s << INDENT << "python_cls.staticmethod(\"" << func->name() << "\");" << endl; + } + + writeCodeSnips(s, cppClass->typeEntry()->codeSnips(), + CodeSnip::End, TypeSystem::TargetLangCode); + + if (!cppClass->isNamespace()) { + // Static methods + if (!staticMethods.isEmpty()) + s << INDENT << "// Static methods" << endl; + + foreach (QString funcName, staticMethods) + s << INDENT << "python_cls.staticmethod(\"" << funcName << "\");" << endl; + } + + // qHash usage + if (!cppClass->typeEntry()->hashFunction().isEmpty()) + writeHashFunction(s, cppClass); + + // implicity conversions + writeImplicitlyConversion(s, cppClass); + + // register object/value type + if (!cppClass->isNamespace()) { + QString className = cppClass->qualifiedCppName(); + const char* funcName = (cppClass->typeEntry()->isObject() || !isCopyable(cppClass)) ? "object" : "value"; + s << INDENT + << "type_manager::instance().register_" + << funcName + << "_type<" << className << " >(\"" + << cppClass->qualifiedCppName() << (cppClass->typeEntry()->isObject() ? "*" : "") << "\");\n"; + } + s << '}' << endl; +} + +void CppGenerator::writeConstructor(QTextStream& s, const AbstractMetaFunction* func) +{ + s << INDENT << "python_cls.def("; + writeConstructorInitialization(s, func); + s << ");" << endl; +} + +void CppGenerator::writeFunctionArgsDef(QTextStream &sOut, + const AbstractMetaFunction *cppFunction) +{ + bool hasDefaultValue = false; + int argUsed = 0; + QString aux; + QTextStream s(&aux); + + foreach (const AbstractMetaArgument *arg, cppFunction->arguments()) { + if (cppFunction->argumentRemoved(arg->argumentIndex() + 1)) + continue; + + if (argUsed > 0) + s << ", "; + + if (!m_disableNamedArgs) + s << "python::arg(\"" << arg->argumentName() << "\")"; + else + s << "python::arg(0)"; + + if (!arg->defaultValueExpression().isEmpty()) { + QString defaultValue = arg->defaultValueExpression(); + bool isPointer = arg->type()->isObject() || + arg->type()->isQObject() || + arg->type()->isNativePointer(); + + if (isPointer && defaultValue == "0") { + defaultValue = "python::object()"; + } else if (arg->type()->isFlags()) { + defaultValue = " (int) " + defaultValue; + } else if (arg->type()->isEnum()) { + QString enumName = arg->type()->minimalSignature(); + QRegExp reg("(.*::)"); + reg.indexIn(enumName); + if (!defaultValue.startsWith(reg.cap(1))) + defaultValue = reg.cap(1) + defaultValue; + } + + s << "=" << defaultValue; + hasDefaultValue = true; + } + argUsed++; + } + + if (hasDefaultValue || ((argUsed > 0) && !m_disableNamedArgs)) + sOut << "," << endl << INDENT << INDENT << "(" << aux << ")"; +} + +void CppGenerator::writeNormalMethodDef(QTextStream& s, const AbstractMetaFunction* func) +{ + s << INDENT << '{' << endl; + { + Indentation indentation(INDENT); + QString wrapperClassName = getWrapperName(func->ownerClass()); + bool needDefaultFunction = func->isVirtual() && !func->isAbstract() && !func->ownerClass()->hasPrivateDestructor(); + QString castName; + + if (needDefaultFunction) + castName = writeFunctionCast(s, func, "_default", func->implementingClass()->qualifiedCppName()); + else + castName = writeFunctionCast(s, func); + + s << INDENT << "python_cls.def(\"" << func->name() << "\", "; + + if (needDefaultFunction) { // add the default function + s << castName << "(&" << wrapperClassName << "::" << func->originalName() << "_default)"; + } else { + if (func->isAbstract()) + s << "python::pure_virtual"; + s << '(' << castName << '(' << getMethodPointerString(func) << "))"; + } + + QString functionPolicy = getFunctionCallPolicy(func); + if (!functionPolicy.isEmpty()) + s << ", " << functionPolicy; + + writeFunctionArgsDef(s, func); + s << ");" << endl; + } + s << INDENT << '}' << endl; +} + +void CppGenerator::writeModifiedMethodDef(QTextStream& s, const AbstractMetaFunction* func) +{ + s << INDENT << '{' << endl; + { + Indentation indentation(INDENT); + QString castName = writeFunctionCast(s, func); + s << INDENT + << "python_cls.def(\"" + << func->name() << "\", " + << castName + << "(&" << func->implementingClass()->name() + << "_" << func->originalName() + << "_modified)"; + QString functionPolicy = getFunctionCallPolicy(func); + if (!functionPolicy.isEmpty()) + s << ", " << functionPolicy; + + writeFunctionArgsDef(s, func); + s << ");" << endl; + } + s << INDENT << '}' << endl; +} + +QString CppGenerator::operatorFunctionName(const AbstractMetaFunction *cppFunction) +{ + QString funcName = QString("%1_operator_%2_") + .arg(cppFunction->arguments()[0]->type()->name()) + .arg(cppFunction->arguments()[1]->type()->name()); + + if (cppFunction->name().contains(">>")) { + funcName += "rshift"; + } else if (cppFunction->name().contains("<<")) { + funcName += "lshift"; + } else { + //TODO: implemente support to others operators + return QString(); + } + + return funcName; +} + +void CppGenerator::writeGlobalOperatorOverloadImpl(QTextStream& s, const AbstractMetaFunction* cppFunction) +{ + Indentation indent(INDENT); + QString operatorStr; + + if (cppFunction->name().contains(">>")) { + operatorStr = " >> "; + } else if (cppFunction->name().contains("<<")) { + operatorStr = " << "; + } else { + //TODO: implemente support to others operators + return; + } + + QString funcName = operatorFunctionName(cppFunction); + bool reverse = cppFunction->isReverseOperator(); + + const AbstractMetaClass *klass = cppFunction->ownerClass(); + s << "python::object " << funcName << "("; + writeFunctionArguments(s, cppFunction, SkipDefaultValues | SkipRemovedArguments); + s << ")" << endl << "{" << endl + << INDENT << cppFunction->arguments()[reverse]->argumentName() + << operatorStr << cppFunction->arguments()[!reverse]->argumentName() << ";" << endl + << INDENT << "return python::object(PySide::ptr(&" + << cppFunction->arguments()[reverse]->argumentName() << "));" << endl + << "}" << endl; +} + +void CppGenerator::writeGlobalOperatorOverload(QTextStream &s, const AbstractMetaFunction *cppFunction) +{ + QString funcName = operatorFunctionName(cppFunction); + if (funcName.isEmpty()) + return; + + bool reverse = cppFunction->isReverseOperator(); + QString operatorStr; + if (cppFunction->name().contains(">>")) { + operatorStr = QString("__%1rshift__").arg(reverse ? "r" : ""); + } else if (cppFunction->name().contains("<<")) { + operatorStr = QString("__%1lshift__").arg(reverse ? "r" : ""); + } else { + //TODO: implemente support to others operators + return; + } + + s << INDENT << "python_cls.def(\"" << operatorStr << "\", " << funcName << ");\n"; +} + +QString CppGenerator::getOperatorArgumentTypeName(const AbstractMetaFunction *cppFunction, int argumentIndex) +{ + AbstractMetaType* type = cppFunction->arguments()[argumentIndex]->type(); + if (type->name() == cppFunction->implementingClass()->name()) + return QLatin1String("python::self"); + + QString typeName = translateType(type, cppFunction->implementingClass(), + (Option)(ExcludeReference)); + return type->isPrimitive() ? "(" + typeName + ")(0)" : "python::other<" + typeName + " >()"; +} + +void CppGenerator::writeOperatorOverload(QTextStream& s, const AbstractMetaFunction* cppFunction) +{ + static QRegExp operatorRegex("operator(.+)"); + + if (!operatorRegex.exactMatch(cppFunction->originalName())) { + qWarning("What kind of operator is that!? %s", + cppFunction->originalName().toLocal8Bit().data()); + return; + } + + QString op(operatorRegex.cap(1)); + if (op == "=" || op == "[]") { + // = is handled by type boost and type conversions, [] by someone... + return; + } + + // no args == member unary operator + if (!cppFunction->arguments().count()) { + // check if it is a name instead of an operator symbol + // this means it is a conversion operator that will be ignored for now + static QRegExp ConversionOperatorRegex("[A-Za-z]+"); + if (ConversionOperatorRegex.indexIn(op) < 0) + s << INDENT << "python_cls.def(" << op << "python::self);" << endl; + return; + } + + //this because global operators use first arg with current class + if (cppFunction->isInGlobalScope()) { + writeGlobalOperatorOverload(s, cppFunction); + return; + } + + QString operand1, operand2; + if (cppFunction->arguments().count() == 1) { + operand1 = "python::self"; + operand2 = getOperatorArgumentTypeName(cppFunction, 0); + } else { + operand1 = getOperatorArgumentTypeName(cppFunction, 0); + operand2 = getOperatorArgumentTypeName(cppFunction, 1); + } + s << INDENT << "python_cls.def(" << operand1 << ' ' << op << ' ' << operand2 << ");\n"; +} + +void CppGenerator::finishGeneration() +{ + //Generate boost wrapper file + QString classFiles; + QTextStream sClassFiles(&classFiles); + QString classPythonDefines; + QTextStream sClassPythonDefines(&classPythonDefines); + + Indentation indent(INDENT); + + foreach (AbstractMetaClass *cls, classes()) { + if (!shouldGenerate(cls) || cls->enclosingClass()) + continue; + + if (m_packageName.isEmpty()) + m_packageName = cls->package(); + + QString wrapperName = getWrapperName(cls); + QString boostFilename; + boostFilename += wrapperName + ".hpp"; + sClassFiles << "#include \"" << boostFilename << "\"" << endl; + + QString define_str = wrapperName + "::"; + define_str += "define_python_class();"; + + sClassPythonDefines << INDENT << define_str << 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 << "#include \"converter_register_" << moduleName().toLower(); + s << ".hpp\"" << endl << endl; + + s << classFiles << endl; + + s << "using namespace boost;" << endl << endl; + s << "using namespace PySide;" << endl << endl; + + s << "// forward decl. for global func. register\n"; + s << "void register_global_functions_" << moduleName().toLower() << "();\n\n"; + + s << "BOOST_PYTHON_MODULE(" << moduleName() << ")" << endl; + s << "{" << endl; + + foreach (QString requiredModule, TypeDatabase::instance()->requiredTargetImports()) { + s << INDENT << "if ("; + s << "PyImport_ImportModule(\"" << requiredModule << "\") == NULL) {" << endl; + s << INDENT << INDENT << "PyErr_SetString(PyExc_ImportError,"; + s << "\"could not import " << requiredModule << "\");" << endl; + s << INDENT << INDENT << "return;" << endl; + s << INDENT << "}" << endl; + } + s << endl; + + s << INDENT << "register_type_converters_" << moduleName().toLower() << "();" << endl << endl + << classPythonDefines << endl + << INDENT << "register_global_functions_" << moduleName().toLower() << "();" << endl + << INDENT << "//Namespaces" << endl; + + + s << "}" << endl << endl; + } + + writeGlobalFunctions(); +} + +void CppGenerator::writeGlobalFunctions() +{ + QString fileName = moduleName().toLower() + "_globals_wrapper.cpp"; + + FileOut fileOut(outputDirectory() + "/" + subDirectoryForPackage(m_packageName) + "/" + fileName); + + QSet<QString> includes; + QString defsStr; + QTextStream defsStream(&defsStr); + + foreach (AbstractMetaFunction* func, globalFunctions()) { + QString incFile = func->includeFile(); + QRegExp regex("\\b" + moduleName() + "\\b"); + //FIXME: this regex doesn't work with all cases, e.g.: + // moduleName() = local + // incFile = /usr/local/include/local + if (regex.indexIn(incFile) == -1) + continue; + + int idx = incFile.indexOf(moduleName()); + QString cleanPath = QDir::cleanPath(incFile.mid(idx)); + if (!cleanPath.startsWith(moduleName())) + continue; + + includes << cleanPath; + defsStream << INDENT << "{\n" << INDENT; + QString castName = writeFunctionCast(defsStream, func); + defsStream << INDENT << INDENT << "python::def(\"" << func->name(); + defsStream << "\", " << castName << '(' << func->name() << ')'; + if (func->type() && func->type()->isReference()) + defsStream << ", python::return_internal_reference<>()"; + defsStream << ");\n"; + defsStream << INDENT << "}\n"; + } + + QTextStream& s = fileOut.stream; + + // write license comment + s << licenseComment() << endl; + + s << "#include \"pyside.hpp\"" << endl; + + foreach (QString include, includes) + s << "#include <" << include << ">\n"; + + s << "using namespace boost;\n\n"; + s << "using namespace PySide;\n\n"; + + // Add module level code snippets to 'Global' class + TypeSystemTypeEntry *moduleEntry = dynamic_cast<TypeSystemTypeEntry *>( + TypeDatabase::instance()->findType(m_packageName)); + QString sEnd; + QTextStream snipEnd(&sEnd); + if (moduleEntry && moduleEntry->codeSnips().size() > 0) { + foreach (CodeSnip snip, moduleEntry->codeSnips()) { + if (snip.position == CodeSnip().Beginning) + snip.formattedCode(s, INDENT); + else + snip.formattedCode(snipEnd, INDENT); + } + } + + s << "\nvoid register_global_functions_" << moduleName().toLower() << "() {\n"; + { //global enums + QString name_space; + + foreach (AbstractMetaEnum *cppEnum, globalEnums()) { + if (cppEnum) + writeEnum(s, cppEnum, name_space); + } + } + s << sEnd; + s << defsStr; + s << "}\n"; +} + |