diff options
-rw-r--r-- | generator/cppgenerator.cpp | 141 | ||||
-rw-r--r-- | generator/cppgenerator.h | 4 | ||||
-rw-r--r-- | generator/headergenerator.cpp | 22 | ||||
-rw-r--r-- | generator/shibokengenerator.cpp | 91 | ||||
-rw-r--r-- | generator/shibokengenerator.h | 10 | ||||
-rw-r--r-- | libshiboken/sbkconverter.cpp | 25 | ||||
-rw-r--r-- | libshiboken/sbkconverter.h | 11 | ||||
-rw-r--r-- | libshiboken/sbkmodule.cpp | 19 | ||||
-rw-r--r-- | libshiboken/sbkmodule.h | 19 | ||||
-rw-r--r-- | tests/minimalbinding/typesystem_minimal.xml | 12 | ||||
-rw-r--r-- | tests/samplebinding/oddbool_test.py | 6 | ||||
-rw-r--r-- | tests/samplebinding/typesystem_sample.xml | 76 |
12 files changed, 387 insertions, 49 deletions
diff --git a/generator/cppgenerator.cpp b/generator/cppgenerator.cpp index ec376d5f4..9fe65ca23 100644 --- a/generator/cppgenerator.cpp +++ b/generator/cppgenerator.cpp @@ -799,7 +799,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun if (func->type() && func->typeReplaced(0) != "PyObject") { // TODO-CONVERTER ----------------------------------------------------------------------- - if (isWrapperType(func->type())) { + if (isWrapperType(func->type()) || isUserPrimitive(func->type())) { s << INDENT << "PythonToCppFunc " PYTHON_TO_CPP_VAR " = " << cpythonIsConvertibleFunction(func->type()); s << PYTHON_RETURN_VAR ");" << endl; s << INDENT << "if (!" PYTHON_TO_CPP_VAR ") {" << endl; @@ -1044,6 +1044,7 @@ void CppGenerator::writeConverterFunctions(QTextStream& s, const AbstractMetaCla QString typeCheck; QString toCppConv; + QString toCppPreConv; if (conv->isConversionOperator()) { const AbstractMetaClass* sourceClass = conv->ownerClass(); typeCheck = QString("PyObject_TypeCheck(pyIn, %1)").arg(cpythonTypeNameExt(sourceClass->typeEntry())); @@ -1055,24 +1056,37 @@ void CppGenerator::writeConverterFunctions(QTextStream& s, const AbstractMetaCla continue; const AbstractMetaType* sourceType = conv->arguments().first()->type(); // TODO-CONVERTER ----------------------------------------------------------------------- - if (isWrapperType(sourceType)) - typeCheck = QString("%1pyIn)").arg(cpythonCheckFunction(sourceType)); - else - // TODO-CONVERTER ----------------------------------------------------------------------- - typeCheck = QString("%1(pyIn)").arg(cpythonCheckFunction(sourceType)); + typeCheck = cpythonCheckFunction(sourceType); if (isWrapperType(sourceType)) { + typeCheck = QString("%1pyIn)").arg(typeCheck); toCppConv = QString("%1%2") .arg((sourceType->isReference() || !isPointerToWrapperType(sourceType)) ? "*" : "") .arg(cpythonWrapperCPtr(sourceType->typeEntry(), "pyIn")); + } else if (typeCheck.contains("%in")) { + typeCheck.replace("%in", "pyIn"); + typeCheck = QString("(%1)").arg(typeCheck); } else { + typeCheck = QString("%1(pyIn)").arg(typeCheck); + } + + if (isUserPrimitive(sourceType)) { + QTextStream pc(&toCppPreConv); + pc << INDENT << getFullTypeNameWithoutModifiers(sourceType) << " cppIn = "; + writeMinimalConstructorExpression(pc, sourceType); + pc << ';' << endl; + writeToCppConversion(pc, sourceType, 0, "pyIn", "cppIn"); + pc << ';'; + toCppConv.append("cppIn"); + } else if (!isWrapperType(sourceType)) { QTextStream tcc(&toCppConv); writeToCppConversion(tcc, sourceType, metaClass, "pyIn", "/*BOZO-1043*/"); } + // TODO-CONVERTER ----------------------------------------------------------------------- } const AbstractMetaType* sourceType = conv->isConversionOperator() ? buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass()) : conv->arguments().first()->type(); - writePythonToCppConversionFunctions(s, sourceType, targetType, typeCheck, toCppConv); + writePythonToCppConversionFunctions(s, sourceType, targetType, typeCheck, toCppConv, toCppPreConv); } writeCustomConverterFunctions(s, customConversion); @@ -1778,7 +1792,7 @@ void CppGenerator::writeTypeCheck(QTextStream& s, const AbstractMetaType* argTyp typeCheck.append(QString("(%1)").arg(argumentName)); // TODO-CONVERTER ----------------------------------------------------------------------- - if (customCheck.isEmpty() && isWrapperType(argType)) { + if (customCheck.isEmpty() && (isWrapperType(argType) || isUserPrimitive(argType))) { typeCheck = QString("(%1 = %2))").arg(pythonToCppConverterForArgumentName(argumentName)).arg(typeCheck); } // TODO-CONVERTER ----------------------------------------------------------------------- @@ -1865,11 +1879,13 @@ void CppGenerator::writePythonToCppTypeConversion(QTextStream& s, QString cppOutAux = QString("%1_local").arg(cppOut); // TODO-CONVERTER ----------------------------------------------------------------------- - if (isWrapperType(type)) { + if (isWrapperType(type) || isUserPrimitive(type)) { bool treatAsPointer = isValueTypeWithCopyConstructorOnly(type); - bool isPointerOrObjectType = isObjectType(type) || isPointer(type); - bool mayHaveImplicitConversion = type->isReference() && !(treatAsPointer || isPointerOrObjectType); + bool isPointerOrObjectType = (isObjectType(type) || isPointer(type)) && /*TODO-CONVERTERS: is this really needed?*/ !isUserPrimitive(type); + bool mayHaveImplicitConversion = type->isReference() + && !isUserPrimitive(type) + && !(treatAsPointer || isPointerOrObjectType); QString typeName = getFullTypeNameWithoutModifiers(type); if (mayHaveImplicitConversion) { s << INDENT << typeName << ' ' << cppOutAux << " = "; @@ -1880,11 +1896,14 @@ void CppGenerator::writePythonToCppTypeConversion(QTextStream& s, s << INDENT << typeName; if (treatAsPointer || isPointerOrObjectType) { s << "* " << cppOut << (defaultValue.isEmpty() ? "" : QString(" = %1").arg(defaultValue)); - } else if (type->isReference()) { + } else if (type->isReference() && !type->typeEntry()->isPrimitive()) { s << "* " << cppOut << " = &" << cppOutAux; } else { s << ' ' << cppOut << " = "; - writeMinimalConstructorExpression(s, type, defaultValue); + if (isUserPrimitive(type)) + writeMinimalConstructorExpression(s, type->typeEntry(), defaultValue); + else + writeMinimalConstructorExpression(s, type, defaultValue); } s << ';' << endl; @@ -2295,6 +2314,7 @@ void CppGenerator::writeCppToPythonFunction(QTextStream& s, const QString& code, QString prettyCode; QTextStream c(&prettyCode); formatCode(c, code, INDENT); + processCodeSnip(prettyCode); s << "static PyObject* " << cppToPythonFunctionName(sourceTypeName, targetTypeName); s << "(const void* cppIn) {" << endl; @@ -2304,6 +2324,7 @@ void CppGenerator::writeCppToPythonFunction(QTextStream& s, const QString& code, void CppGenerator::writeCppToPythonFunction(QTextStream& s, const CustomConversion* customConversion) { QString code = customConversion->nativeToTargetConversion(); + processCodeSnip(code); code.prepend(QString("::%1& cppInRef = *((::%1*)cppIn);\n").arg(customConversion->ownerType()->qualifiedCppName())); code.replace("%INTYPE", cpythonTypeNameExt(customConversion->ownerType())); code.replace("%OUTTYPE", "PyObject*"); @@ -2317,6 +2338,7 @@ void CppGenerator::writePythonToCppFunction(QTextStream& s, const QString& code, QString prettyCode; QTextStream c(&prettyCode); formatCode(c, code, INDENT); + processCodeSnip(prettyCode); s << "static void " << pythonToCppFunctionName(sourceTypeName, targetTypeName); s << "(PyObject* pyIn, void* cppOut) {" << endl; s << prettyCode; @@ -2353,7 +2375,8 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, const AbstractMetaType* sourceType, const AbstractMetaType* targetType, QString typeCheck, - QString conversion) + QString conversion, + QString preConversion) { QString sourcePyType = cpythonTypeNameExt(sourceType); @@ -2362,6 +2385,8 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, QTextStream c(&code); if (conversion.isEmpty()) conversion = QString("*%1").arg(cpythonWrapperCPtr(sourceType->typeEntry(), "pyIn")); + if (!preConversion.isEmpty()) + c << INDENT << preConversion << endl; c << INDENT << QString("*((::%1*)cppOut) = ::%1(%2);") .arg(targetType->typeEntry()->qualifiedCppName()) .arg(conversion); @@ -2399,14 +2424,31 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, // Python to C++ convertible check function. QString typeCheck = toNative->sourceTypeCheck(); if (typeCheck.isEmpty()) { + QString pyTypeName = toNative->sourceTypeName(); + if (pyTypeName == "Py_None" || pyTypeName == "PyNone") + typeCheck = "%in == Py_None"; + else if (pyTypeName == "SbkEnumType") + typeCheck = "Shiboken::isShibokenEnum(%in)"; + else if (pyTypeName == "SbkObject") + typeCheck = "Shiboken::Object::checkType(%in)"; + else if (pyTypeName == "PyTypeObject") + typeCheck = "PyType_Check(%in)"; + else if (pyTypeName == "PyObject") + typeCheck = "PyObject_TypeCheck(%in, &PyBaseObject_Type)"; + else if (pyTypeName.startsWith("Py")) + typeCheck= QString("%1_Check(%in)").arg(pyTypeName); + } + if (typeCheck.isEmpty()) { if (!toNative->sourceType() || toNative->sourceType()->isPrimitive()) { - QString errorMsg = "User added implicit conversions must provide either a input type check function or a non primitive type entry."; - ReportHandler::warning(errorMsg); - s << "#error " << errorMsg << endl; + qFatal(qPrintable(QString("User added implicit conversion for C++ type '%1' must provide either an input "\ + "type check function or a non primitive type entry.") + .arg(targetType->qualifiedCppName())), NULL); + } typeCheck = QString("PyObject_TypeCheck(%in, %1)").arg(cpythonTypeNameExt(toNative->sourceType())); } typeCheck.replace("%in", "pyIn"); + processCodeSnip(typeCheck); writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); } @@ -2913,6 +2955,22 @@ void CppGenerator::writeSpecialCastFunction(QTextStream& s, const AbstractMetaCl s << "}\n\n"; } +void CppGenerator::writePrimitiveConverterInitialization(QTextStream& s, const CustomConversion* customConversion) +{ + const TypeEntry* type = customConversion->ownerType(); + s << INDENT << "// Register converter for type '" << type->qualifiedTargetLangName() << "'." << endl; + s << INDENT << converterObject(type) << " = Shiboken::Conversions::createConverter("; + if (type->targetLangApiName() == type->name()) + s << '0'; + else if (type->targetLangApiName() == "PyObject") + s << "&PyBaseObject_Type"; + else + s << '&' << type->targetLangApiName() << "_Type"; + QString typeName = fixedCppTypeName(type); + s << ", " << cppToPythonFunctionName(typeName, typeName) << ");" << endl; + writeCustomConverterRegister(s, customConversion, converterObject(type)); +} + void CppGenerator::writeExtendedConverterInitialization(QTextStream& s, const TypeEntry* externalType, const QList<const AbstractMetaClass*>& conversions) { s << INDENT << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << endl; @@ -3401,7 +3459,7 @@ void CppGenerator::writeSetterFunction(QTextStream& s, const AbstractMetaField* AbstractMetaType* fieldType = metaField->type(); // TODO-CONVERTER ----------------------------------------------------------------------- - if (isWrapperType(fieldType)) { + if (isWrapperType(fieldType) || isUserPrimitive(fieldType)) { s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ';' << endl; } // TODO-CONVERTER ----------------------------------------------------------------------- @@ -3419,7 +3477,7 @@ void CppGenerator::writeSetterFunction(QTextStream& s, const AbstractMetaField* // TODO-CONVERTER ----------------------------------------------------------------------- s << INDENT; - if (isWrapperType(fieldType)) { + if (isWrapperType(fieldType) || isUserPrimitive(fieldType)) { if (avoidProtectedHack() && metaField->isProtected()) { s << getFullTypeNameWithoutModifiers(fieldType); s << (fieldType->indirections() == 1 ? "*" : "") << " cppOut;" << endl; @@ -4311,6 +4369,9 @@ void CppGenerator::finishGeneration() s << "// Current module's type array." << endl; s << "PyTypeObject** " << cppApiVariableName() << ';' << endl; + s << "// Current module's converter array." << endl; + s << "SbkConverter** " << convertersVariableName() << ';' << endl; + CodeSnipList snips; if (moduleEntry) snips = moduleEntry->codeSnips(); @@ -4374,8 +4435,10 @@ void CppGenerator::finishGeneration() QStringList requiredModules = typeDb->requiredTargetImports(); if (!requiredModules.isEmpty()) s << "// Required modules' type and converter arrays." << endl; - foreach (const QString& requiredModule, requiredModules) + foreach (const QString& requiredModule, requiredModules) { s << "PyTypeObject** " << cppApiVariableName(requiredModule) << ';' << endl; + s << "SbkConverter** " << convertersVariableName(requiredModule) << ';' << endl; + } s << endl; s << "// Module initialization "; @@ -4396,6 +4459,16 @@ void CppGenerator::finishGeneration() s << endl; } + QList<const CustomConversion*> typeConversions = getNonWrapperCustomConversions(); + if (!typeConversions.isEmpty()) { + s << endl << "// Primitive and Container Type converters." << endl << endl; + foreach (const CustomConversion* conversion, typeConversions) { + s << "// C++ to Python conversion for type '" << conversion->ownerType()->qualifiedCppName() << "'." << endl; + writeCppToPythonFunction(s, conversion); + writeCustomConverterFunctions(s, conversion); + } + s << endl; + } s << "#if defined _WIN32 || defined __CYGWIN__" << endl; s << " #define SBK_EXPORT_MODULE __declspec(dllexport)" << endl; @@ -4438,6 +4511,7 @@ void CppGenerator::finishGeneration() s << INDENT << "SBK_MODULE_INIT_ERROR;" << endl; } s << INDENT << cppApiVariableName(requiredModule) << " = Shiboken::Module::getTypes(requiredModule);" << endl; + s << INDENT << convertersVariableName(requiredModule) << " = Shiboken::Module::getTypeConverters(requiredModule);" << endl; } s << INDENT << "}" << endl << endl; } @@ -4449,6 +4523,10 @@ void CppGenerator::finishGeneration() s << INDENT << cppApiVariableName() << " = cppApi;" << endl << endl; } + s << INDENT << "// Create an array of primitive type converters for the current module." << endl; + s << INDENT << "static SbkConverter* sbkConverters[SBK_" << moduleName() << "_CONVERTERS_IDX_COUNT" << "];" << endl; + s << INDENT << convertersVariableName() << " = sbkConverters;" << endl << endl; + s << "#ifdef IS_PY3K" << endl; s << INDENT << "PyObject* module = Shiboken::Module::create(\"" << moduleName() << "\", &moduledef);" << endl; s << "#else" << endl; @@ -4456,13 +4534,26 @@ void CppGenerator::finishGeneration() s << moduleName() << "_methods);" << endl; s << "#endif" << endl << endl; + //s << INDENT << "// Initialize converters for primitive types." << endl; + //s << INDENT << "initConverters();" << endl << endl; + s << INDENT << "// Initialize classes in the type system" << endl; s << classPythonDefines; - s << endl; - foreach (const TypeEntry* externalType, extendedConverters.keys()) { - writeExtendedConverterInitialization(s, externalType, extendedConverters[externalType]); + if (!typeConversions.isEmpty()) { + s << endl; + foreach (const CustomConversion* conversion, typeConversions) { + writePrimitiveConverterInitialization(s, conversion); + s << endl; + } + } + + if (!extendedConverters.isEmpty()) { s << endl; + foreach (const TypeEntry* externalType, extendedConverters.keys()) { + writeExtendedConverterInitialization(s, externalType, extendedConverters[externalType]); + s << endl; + } } writeEnumsInitialization(s, globalEnums); @@ -4494,8 +4585,10 @@ void CppGenerator::finishGeneration() foreach (QByteArray type, typeResolvers) s << INDENT << typeResolverString(type) << ';' << endl; + s << endl; if (maxTypeIndex) - s << endl << INDENT << "Shiboken::Module::registerTypes(module, " << cppApiVariableName() << ");" << endl; + s << INDENT << "Shiboken::Module::registerTypes(module, " << cppApiVariableName() << ");" << endl; + s << INDENT << "Shiboken::Module::registerTypeConverters(module, " << convertersVariableName() << ");" << endl; s << endl << INDENT << "if (PyErr_Occurred()) {" << endl; { diff --git a/generator/cppgenerator.h b/generator/cppgenerator.h index f4ff188df..86f434cfa 100644 --- a/generator/cppgenerator.h +++ b/generator/cppgenerator.h @@ -181,7 +181,8 @@ private: const AbstractMetaType* sourceType, const AbstractMetaType* targetType, QString typeCheck = QString(), - QString conversion = QString()); + QString conversion = QString(), + QString preConversion = QString()); /// Writes a pair of Python to C++ conversion and check functions for implicit conversions. void writePythonToCppConversionFunctions(QTextStream& s, const CustomConversion::TargetToNativeConversion* toNative, @@ -240,6 +241,7 @@ private: /// Writes the implementation of special cast functions, used when we need to cast a class with multiple inheritance. void writeSpecialCastFunction(QTextStream& s, const AbstractMetaClass* metaClass); + void writePrimitiveConverterInitialization(QTextStream& s, const CustomConversion* customConversion); void writeExtendedConverterInitialization(QTextStream& s, const TypeEntry* externalType, const QList<const AbstractMetaClass*>& conversions); void writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, bool userHeuristicForReturn); diff --git a/generator/headergenerator.cpp b/generator/headergenerator.cpp index 0de8c44ef..e6668853c 100644 --- a/generator/headergenerator.cpp +++ b/generator/headergenerator.cpp @@ -360,8 +360,28 @@ void HeaderGenerator::finishGeneration() macrosStream << "SBK_"+moduleName()+"_IDX_COUNT"; macrosStream.setFieldWidth(0); macrosStream << ' ' << getMaxTypeIndex() << endl << endl; - macrosStream << "// This variable stores all python types exported by this module" << endl; + macrosStream << "// This variable stores all Python types exported by this module." << endl; macrosStream << "extern PyTypeObject** " << cppApiVariableName() << ';' << endl << endl; + macrosStream << "// This variable stores all type converters exported by this module." << endl; + macrosStream << "extern SbkConverter** " << convertersVariableName() << ';' << endl << endl;; + + // TODO-CONVERTER ------------------------------------------------------------------------------ + // Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex(). + macrosStream << "// Converter indices" << endl; + QList<const PrimitiveTypeEntry*> primitives = primitiveTypes(); + int pCount = 0; + foreach (const PrimitiveTypeEntry* ptype, primitives) { + if (!ptype->generateCode() || !isUserPrimitive(ptype)) + continue; + _writeTypeIndexDefineLine(macrosStream, getTypeIndexVariableName(ptype), pCount); + pCount++; + } + // Because on win32 the compiler will not accept a zero length array. + if (pCount == 0) + pCount++; + _writeTypeIndexDefineLine(macrosStream, QString("SBK_%1_CONVERTERS_IDX_COUNT").arg(moduleName()), pCount); + macrosStream << endl; + // TODO-CONVERTER ------------------------------------------------------------------------------ macrosStream << "// Macros for type check" << endl; foreach (const AbstractMetaEnum* cppEnum, globalEnums) { diff --git a/generator/shibokengenerator.cpp b/generator/shibokengenerator.cpp index c344f3c8f..19a8f7da8 100644 --- a/generator/shibokengenerator.cpp +++ b/generator/shibokengenerator.cpp @@ -591,7 +591,7 @@ void ShibokenGenerator::writeToPythonConversion(QTextStream& s, const AbstractMe const AbstractMetaClass* context, const QString& argumentName) { // TODO-CONVERTER ----------------------------------------------------------------------- - if (isWrapperType(type)) { + if (isWrapperType(type) || isUserPrimitive(type)) { s << cpythonToPythonConversionFunction(type) << argumentName << ')'; return; } @@ -612,7 +612,7 @@ void ShibokenGenerator::writeToCppConversion(QTextStream& s, const AbstractMetaT const QString& inArgName, const QString& outArgName) { // TODO-CONVERTER ----------------------------------------------------------------------- - if (isWrapperType(type)) { + if (isWrapperType(type) || isUserPrimitive(type)) { s << cpythonToCppConversionFunction(type, context) << inArgName << ", &" << outArgName << ')'; return; } @@ -764,6 +764,15 @@ QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntry* type) return cppApiVariableName(type->targetLangPackage()) + '[' + getTypeIndexVariableName(type) + ']'; } +QString ShibokenGenerator::converterObject(const AbstractMetaType* type) +{ + return converterObject(type->typeEntry()); +} +QString ShibokenGenerator::converterObject(const TypeEntry* type) +{ + return convertersVariableName(type->targetLangPackage()) + '[' + getTypeIndexVariableName(type) + ']'; +} + QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType* type) { return cppApiVariableName(type->typeEntry()->targetLangPackage()) + '[' + getTypeIndexVariableName(type) + ']'; @@ -946,6 +955,23 @@ bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const AbstractMetaTyp return isValueTypeWithCopyConstructorOnly(type->typeEntry()); } +bool ShibokenGenerator::isUserPrimitive(const TypeEntry* type) +{ + if (!type->isPrimitive()) + return false; + const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; + if (trueType->basicAliasedTypeEntry()) + trueType = trueType->basicAliasedTypeEntry(); + return trueType->isPrimitive() && !trueType->isCppPrimitive() && trueType->qualifiedCppName() != "std::string"; +} + +bool ShibokenGenerator::isUserPrimitive(const AbstractMetaType* type) +{ + if (type->indirections() != 0) + return false; + return isUserPrimitive(type->typeEntry()); +} + bool ShibokenGenerator::shouldDereferenceArgumentPointer(const AbstractMetaArgument* arg) { return shouldDereferenceAbstractMetaTypePointer(arg->type()); @@ -978,9 +1004,8 @@ QString ShibokenGenerator::cpythonCheckFunction(const AbstractMetaType* metaType } // TODO-CONVERTER ----------------------------------------------------------------------- - if (isWrapperType(metaType)) { - return QString("SbkObject_TypeCheck(%1, ").arg(cpythonTypeNameExt(metaType->typeEntry())); - } + if (isWrapperType(metaType) || isUserPrimitive(metaType)) + return cpythonCheckFunction(metaType->typeEntry(), genericNumberType); // TODO-CONVERTER ----------------------------------------------------------------------- QString baseName = cpythonBaseName(metaType); @@ -1011,6 +1036,11 @@ QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry* type, bool gene // TODO-CONVERTER ----------------------------------------------------------------------- if (isWrapperType(type)) { return QString("SbkObject_TypeCheck(%1, ").arg(cpythonTypeNameExt(type)); + } else if (isUserPrimitive(type)) { + QString typeCheck; + if (!type->targetLangApiName().isEmpty()) + typeCheck = QString("%1_Check").arg(type->targetLangApiName()); + return typeCheck; } // TODO-CONVERTER ----------------------------------------------------------------------- @@ -1064,6 +1094,9 @@ QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntry* type, b : "isPythonToCppPointerConvertible"; return QString("Shiboken::Conversions::%1((SbkObjectType*)%2, ") .arg(isConv).arg(cpythonTypeNameExt(type)); + } else if (isUserPrimitive(type)) { + return QString("Shiboken::Conversions::isPythonToCppConvertible(%1, ") + .arg(converterObject(type)); } // TODO-CONVERTER ----------------------------------------------------------------------- @@ -1101,6 +1134,9 @@ QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaType* isConv = "isPythonToCppValueConvertible"; return QString("Shiboken::Conversions::%1((SbkObjectType*)%2, ") .arg(isConv).arg(cpythonTypeNameExt(metaType)); + } else if (isUserPrimitive(metaType)) { + return QString("Shiboken::Conversions::isPythonToCppConvertible(%1, ") + .arg(converterObject(metaType)); } // TODO-CONVERTER ----------------------------------------------------------------------- @@ -1132,6 +1168,9 @@ QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaType return QString("Shiboken::Conversions::pythonToCpp%1((SbkObjectType*)%2, ") .arg(isPointer(type) ? "Pointer" : "Copy") .arg(cpythonTypeNameExt(type)); + } else if (isUserPrimitive(type)) { + return QString("Shiboken::Conversions::pythonToCpp(%1, ") + .arg(converterObject(type)); } // TODO-CONVERTER ----------------------------------------------------------------------- QString base; @@ -1153,6 +1192,8 @@ QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaT conversion = "pointer"; return QString("Shiboken::Conversions::%1ToPython((SbkObjectType*)%2, %3") .arg(conversion).arg(cpythonTypeNameExt(type)).arg(conversion == "pointer" ? "" : "&"); + } else if (isUserPrimitive(type)) { + return QString("Shiboken::Conversions::copyToPython(%1, &").arg(converterObject(type)); } // TODO-CONVERTER ----------------------------------------------------------------------- // exclude const on Objects @@ -1179,6 +1220,8 @@ QString ShibokenGenerator::cpythonToPythonConversionFunction(const TypeEntry* ty conversion = "pointer"; return QString("Shiboken::Conversions::%1ToPython((SbkObjectType*)%2, %3") .arg(conversion).arg(cpythonTypeNameExt(type)).arg(conversion == "pointer" ? "" : "&"); + } else if (isUserPrimitive(type)) { + return QString("Shiboken::Conversions::copyToPython(%1, &").arg(converterObject(type)); } // TODO-CONVERTER ----------------------------------------------------------------------- QString base; @@ -1373,6 +1416,17 @@ ShibokenGenerator::ExtendedConverterData ShibokenGenerator::getExtendedConverter return extConvs; } +QList<const CustomConversion*> ShibokenGenerator::getNonWrapperCustomConversions() +{ + QList<const CustomConversion*> conversions; + foreach (const PrimitiveTypeEntry* type, primitiveTypes()) { + if (!shouldGenerateTypeEntry(type) || !isUserPrimitive(type) || !type->customConversion()) + continue; + conversions << type->customConversion(); + } + return conversions; +} + static QString getArgumentsFromMethodCall(const QString& str) { // It would be way nicer to be able to use a Perl like @@ -1780,7 +1834,7 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa if (conversionType) { switch (converterVariable) { case TypeSystemToCppFunction: { - if (!isWrapperType(conversionType)) { + if (!isWrapperType(conversionType) && !isUserPrimitive(conversionType)) { c << list.at(1) << list.at(2) << " = "; c << cpythonToCppConversionFunction(conversionType); c << '('; @@ -1821,21 +1875,32 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa if (conversion.isEmpty()) conversion = cpythonToPythonConversionFunction(conversionType); default: { - if (!isWrapperType(conversionType)) { + // TODO-CONVERTER ----------------------------------------------------------------------- + if (!isWrapperType(conversionType) && !isUserPrimitive(conversionType)) { c << '('; break; } + // TODO-CONVERTER ----------------------------------------------------------------------- QString arg = getConverterTypeSystemVariableArgument(code, pos); conversionString += arg; if (converterVariable == TypeSystemToPythonFunction && !isVariable(arg)) { qFatal(qPrintable(QString("Only variables are acceptable as argument to %%CONVERTTOPYTHON type system variable on code snippet: '%1'") .arg(code)), NULL); } - c << arg; + if (conversion.contains("%in")) { + conversion.prepend('('); + conversion.replace("%in", arg); + } else { + if (conversionType->isPrimitive() && converterVariable == TypeSystemCheckFunction) + conversion.append('('); + c << arg; + } } } } else { - conversion = QString("Shiboken::Converter< %1 >::%2(").arg(conversionTypeName).arg(conversionName); + if (list.count() > 2) + c << list.at(1) << list.at(2) << " = "; + c << QString("Shiboken::Converter< %1 >::%2(").arg(conversionTypeName).arg(conversionName); } replacements.append(qMakePair(conversionString, conversion)); } @@ -2226,6 +2291,14 @@ QString ShibokenGenerator::cppApiVariableName(const QString& moduleName) const return result; } +QString ShibokenGenerator::convertersVariableName(const QString& moduleName) const +{ + QString result = cppApiVariableName(moduleName); + result.chop(1); + result.append("Converters"); + return result; +} + static QString processInstantiationsVariableName(const AbstractMetaType* type) { QString res = QString("_%1").arg(_fixedCppTypeName(type->typeEntry()->qualifiedCppName()).toUpper()); diff --git a/generator/shibokengenerator.h b/generator/shibokengenerator.h index 05e5bd929..00de95860 100644 --- a/generator/shibokengenerator.h +++ b/generator/shibokengenerator.h @@ -309,6 +309,9 @@ public: bool isValueTypeWithCopyConstructorOnly(const TypeEntry* type) const; bool isValueTypeWithCopyConstructorOnly(const AbstractMetaType* type) const; + /// Returns true if the type is a primitive but not a C++ primitive. + static bool isUserPrimitive(const TypeEntry* type); + static bool isUserPrimitive(const AbstractMetaType* type); /// Checks if an argument type should be dereferenced by the Python method wrapper before calling the C++ method. static bool shouldDereferenceArgumentPointer(const AbstractMetaArgument* arg); @@ -317,6 +320,9 @@ public: static bool visibilityModifiedToPrivate(const AbstractMetaFunction* func); + QString converterObject(const AbstractMetaType* type); + QString converterObject(const TypeEntry* type); + QString cpythonBaseName(const AbstractMetaClass* metaClass); QString cpythonBaseName(const TypeEntry* type); QString cpythonBaseName(const AbstractMetaType* type); @@ -404,6 +410,7 @@ public: /// Returns true if the generated code should use the "#define protected public" hack. bool avoidProtectedHack() const; QString cppApiVariableName(const QString& moduleName = QString()) const; + QString convertersVariableName(const QString& moduleName = QString()) const; /** * Returns the type index variable name for a given class. If \p alternativeTemplateName is true * and the class is a typedef for a template class instantiation, it will return an alternative name @@ -490,6 +497,9 @@ protected: /// Returns all extended conversions for the current module. ExtendedConverterData getExtendedConverters() const; + /// Returns a list of converters for the non wrapper types of the current module. + QList<const CustomConversion*> getNonWrapperCustomConversions(); + /// Returns true if the Python wrapper for the received OverloadData must accept a list of arguments. static bool pythonFunctionWrapperUsesListOfArguments(const OverloadData& overloadData); diff --git a/libshiboken/sbkconverter.cpp b/libshiboken/sbkconverter.cpp index b252e16c7..c5e64f3dd 100644 --- a/libshiboken/sbkconverter.cpp +++ b/libshiboken/sbkconverter.cpp @@ -41,7 +41,8 @@ static SbkConverter* createConverterObject(PyTypeObject* type, converter->pointerToPython = pointerToPythonFunc; converter->copyToPython = copyToPythonFunc; - converter->toCppPointerConversion = std::make_pair(toCppPointerCheckFunc, toCppPointerConvFunc); + if (toCppPointerCheckFunc && toCppPointerConvFunc) + converter->toCppPointerConversion = std::make_pair(toCppPointerCheckFunc, toCppPointerConvFunc); converter->toCppConversions.clear(); return converter; @@ -60,6 +61,11 @@ SbkConverter* createConverter(SbkObjectType* type, return converter; } +SbkConverter* createConverter(PyTypeObject* type, CppToPythonFunc toPythonFunc) +{ + return createConverterObject(type, 0, 0, 0, toPythonFunc); +} + void deleteConverter(SbkConverter* converter) { if (converter) { @@ -178,16 +184,27 @@ void pythonToCppPointer(SbkObjectType* type, PyObject* pyIn, void* cppOut) *((void**)cppOut) = (pyIn == Py_None) ? 0 : cppPointer((PyTypeObject*)type, (SbkObject*)pyIn); } -void pythonToCppCopy(SbkObjectType* type, PyObject* pyIn, void* cppOut) +static void _pythonToCppCopy(SbkConverter* converter, PyObject* pyIn, void* cppOut) { - assert(type); + assert(converter); assert(pyIn); assert(cppOut); - PythonToCppFunc toCpp = IsPythonToCppConvertible(type->d->converter, pyIn); + PythonToCppFunc toCpp = IsPythonToCppConvertible(converter, pyIn); if (toCpp) toCpp(pyIn, cppOut); } +void pythonToCppCopy(SbkObjectType* type, PyObject* pyIn, void* cppOut) +{ + assert(type); + _pythonToCppCopy(type->d->converter, pyIn, cppOut); +} + +void pythonToCpp(SbkConverter* converter, PyObject* pyIn, void* cppOut) +{ + _pythonToCppCopy(converter, pyIn, cppOut); +} + bool isImplicitConversion(SbkObjectType* type, PythonToCppFunc toCppFunc) { // This is the Object Type or Value Type conversion that only diff --git a/libshiboken/sbkconverter.h b/libshiboken/sbkconverter.h index d7be25776..1d5392488 100644 --- a/libshiboken/sbkconverter.h +++ b/libshiboken/sbkconverter.h @@ -102,6 +102,14 @@ LIBSHIBOKEN_API SbkConverter* createConverter(SbkObjectType* type, CppToPythonFunc pointerToPythonFunc, CppToPythonFunc copyToPythonFunc = 0); +/** + * Creates a converter for a non wrapper type (primitive or container type). + * \param type Python type representing to the new converter. + * \param toPythonFunc Function to convert a C++ object to a Python \p type. + * \returns A new type converter. + */ +LIBSHIBOKEN_API SbkConverter* createConverter(PyTypeObject* type, CppToPythonFunc toPythonFunc); + LIBSHIBOKEN_API void deleteConverter(SbkConverter* converter); /** @@ -187,6 +195,9 @@ LIBSHIBOKEN_API void pythonToCppPointer(SbkObjectType* type, PyObject* pyIn, voi /// Converts a Python object \p pyIn to C++ and copies the result in the C++ variable passed in \p cppOut. LIBSHIBOKEN_API void pythonToCppCopy(SbkObjectType* type, PyObject* pyIn, void* cppOut); +/// Converts a Python object \p pyIn to C++, copying the result in the C++ variable passed in \p cppOut. +LIBSHIBOKEN_API void pythonToCpp(SbkConverter* converter, PyObject* pyIn, void* cppOut); + /** * Helper function returned by generated convertible checking functions * that returns a C++ NULL when the input Python object is None. diff --git a/libshiboken/sbkmodule.cpp b/libshiboken/sbkmodule.cpp index 83c431cde..6f3594e77 100644 --- a/libshiboken/sbkmodule.cpp +++ b/libshiboken/sbkmodule.cpp @@ -32,8 +32,12 @@ /// This hash maps module objects to arrays of Python types. typedef google::dense_hash_map<PyObject*, PyTypeObject**> ModuleTypesMap; +/// This hash maps module objects to arrays of converters. +typedef google::dense_hash_map<PyObject*, SbkConverter**> ModuleConvertersMap; + /// All types produced in imported modules are mapped here. static ModuleTypesMap moduleTypes; +static ModuleConvertersMap moduleConverters; namespace Shiboken { @@ -45,6 +49,8 @@ void init() // Initializes type registry for modules. moduleTypes.set_empty_key((ModuleTypesMap::key_type)0); moduleTypes.set_deleted_key((ModuleTypesMap::key_type)1); + moduleConverters.set_empty_key((ModuleConvertersMap::key_type)0); + moduleConverters.set_deleted_key((ModuleConvertersMap::key_type)1); } PyObject* import(const char* moduleName) @@ -85,4 +91,17 @@ PyTypeObject** getTypes(PyObject* module) return (iter == moduleTypes.end()) ? 0 : iter->second; } +void registerTypeConverters(PyObject* module, SbkConverter** converters) +{ + ModuleConvertersMap::iterator iter = moduleConverters.find(module); + if (iter == moduleConverters.end()) + moduleConverters.insert(std::make_pair(module, converters)); +} + +SbkConverter** getTypeConverters(PyObject* module) +{ + ModuleConvertersMap::iterator iter = moduleConverters.find(module); + return (iter == moduleConverters.end()) ? 0 : iter->second; +} + } } // namespace Shiboken::Module diff --git a/libshiboken/sbkmodule.h b/libshiboken/sbkmodule.h index f089ad8d4..cb5656fca 100644 --- a/libshiboken/sbkmodule.h +++ b/libshiboken/sbkmodule.h @@ -42,6 +42,11 @@ } #endif +extern "C" +{ +struct SbkConverter; +} + namespace Shiboken { namespace Module { @@ -74,6 +79,20 @@ LIBSHIBOKEN_API void registerTypes(PyObject* module, PyTypeObject** types); */ LIBSHIBOKEN_API PyTypeObject** getTypes(PyObject* module); +/** + * Registers the list of converters created by \p module for non-wrapper types. + * \param module Module where the converters were created. + * \param converters Array of SbkConverter* objects representing the converters created on \p module. + */ +LIBSHIBOKEN_API void registerTypeConverters(PyObject* module, SbkConverter** converters); + +/** + * Retrieves the array of converters. + * \param module Module where the converters were created. + * \returns A pointer to the SbkConverter* array of converters. + */ +LIBSHIBOKEN_API SbkConverter** getTypeConverters(PyObject* module); + } } // namespace Shiboken::Module #endif // SBK_MODULE_H diff --git a/tests/minimalbinding/typesystem_minimal.xml b/tests/minimalbinding/typesystem_minimal.xml index 1b6f18de4..d4f673d2d 100644 --- a/tests/minimalbinding/typesystem_minimal.xml +++ b/tests/minimalbinding/typesystem_minimal.xml @@ -4,8 +4,17 @@ <primitive-type name="int"/> <primitive-type name="MinBool" target-lang-api-name="PyBool" default-constructor="MinBool(false)"> - <conversion-rule file="minbool_conversions.h"/> <include file-name="minbool.h" location="global"/> + <conversion-rule file="minbool_conversions.h"> + <native-to-target> + return PyBool_FromLong(%in.value()); + </native-to-target> + <target-to-native> + <add-conversion type="PyBool" check="PyBool_Check(%in)"> + %out = %OUTTYPE(%in == Py_True); + </add-conversion> + </target-to-native> + </conversion-rule> </primitive-type> <container-type name="std::list" type="list"> @@ -20,4 +29,3 @@ <value-type name="ListUser"/> <value-type name="MinBoolUser"/> </typesystem> - diff --git a/tests/samplebinding/oddbool_test.py b/tests/samplebinding/oddbool_test.py index 43727d65b..12b7a5a49 100644 --- a/tests/samplebinding/oddbool_test.py +++ b/tests/samplebinding/oddbool_test.py @@ -62,6 +62,12 @@ class OddBoolTest(unittest.TestCase): self.assertTrue(obu.oddBool()) obu = OddBoolUser(False) self.assertFalse(obu.oddBool()) + cpx = complex(1.0, 0.0) + obu = OddBoolUser(cpx) + self.assertTrue(obu.oddBool()) + cpx = complex(0.0, 0.0) + obu = OddBoolUser(cpx) + self.assertFalse(obu.oddBool()) if __name__ == '__main__': unittest.main() diff --git a/tests/samplebinding/typesystem_sample.xml b/tests/samplebinding/typesystem_sample.xml index 122456184..ec4ace7d7 100644 --- a/tests/samplebinding/typesystem_sample.xml +++ b/tests/samplebinding/typesystem_sample.xml @@ -19,25 +19,85 @@ <primitive-type name="std::string"/> <primitive-type name="std::size_t" target-lang-api-name="PyLong"> + <conversion-rule> + <native-to-target> + return PyLong_FromSize_t(%in); + </native-to-target> + <target-to-native> + <add-conversion type="PyLong"> + %out = %OUTTYPE(PyLong_AsSsize_t(%in)); + </add-conversion> + </target-to-native> + </conversion-rule> </primitive-type> + <primitive-type name="Complex" target-lang-api-name="PyComplex"> - <conversion-rule file="complex_conversions.h"/> <include file-name="complex.h" location="global"/> + <conversion-rule file="complex_conversions.h"> + <native-to-target> + return PyComplex_FromDoubles(%in.real(), %in.imag()); + </native-to-target> + <target-to-native> + <!-- The 'check' attribute can be derived from the 'type' attribute, + it is defined here to test the CHECKTYPE type system variable. --> + <add-conversion type="PyComplex" check="%CHECKTYPE[Complex](%in)"> + double real = PyComplex_RealAsDouble(%in); + double imag = PyComplex_ImagAsDouble(%in); + %out = %OUTTYPE(real, imag); + </add-conversion> + </target-to-native> + </conversion-rule> </primitive-type> <primitive-type name="Null"> - <conversion-rule file="null_conversions.h"/> <include file-name="null.h" location="global"/> + <conversion-rule file="null_conversions.h"> + <native-to-target> + SBK_UNUSED(%in); + Py_RETURN_NONE; + </native-to-target> + <target-to-native> + <add-conversion type="PyObject" check="%in == 0 || %in == Py_None"> + %out = %OUTTYPE(%in == 0); + </add-conversion> + </target-to-native> + </conversion-rule> </primitive-type> - <primitive-type name="HANDLE"> - <conversion-rule file="handle_conversions.h"/> + <primitive-type name="HANDLE" target-lang-api-name="PyComplex"> <include file-name="handle.h" location="local"/> + <conversion-rule file="handle_conversions.h"> + <native-to-target> + return PyCObject_FromVoidPtr(%in, 0); + </native-to-target> + <target-to-native> + <add-conversion type="PyCObject"> + %out = (%OUTTYPE)PyCObject_AsVoidPtr(%in); + </add-conversion> + </target-to-native> + </conversion-rule> </primitive-type> <primitive-type name="OddBool" target-lang-api-name="PyBool" default-constructor="OddBool(false)"> - <conversion-rule file="oddbool_conversions.h"/> <include file-name="oddbool.h" location="global"/> + <include file-name="complex.h" location="global"/> + <conversion-rule file="oddbool_conversions.h"> + <native-to-target> + return PyBool_FromLong(%in.value()); + </native-to-target> + <target-to-native> + <add-conversion type="PyBool"> + // Tests CONVERTTOCPP macro with C++ primitive type. + bool b = %CONVERTTOCPP[bool](%in); + %out = %OUTTYPE(b); + </add-conversion> + <add-conversion type="PyComplex"> + // Tests CONVERTTOCPP macro with user's primitive type. + Complex cpx = %CONVERTTOCPP[Complex](%in); + %out = %OUTTYPE(cpx.real() != 0.0 || cpx.imag() != 0.0); + </add-conversion> + </target-to-native> + </conversion-rule> </primitive-type> <container-type name="std::pair" type="pair"> @@ -1559,10 +1619,10 @@ <value-type name="ByteArray" hash-function="ByteArray::hash"> <conversion-rule file="bytearray_conversions.h"> <target-to-native> - <add-conversion type='Py_None' check='%in == Py_None'> + <add-conversion type="Py_None"> %out = %OUTTYPE(); </add-conversion> - <add-conversion type='PyString' check='PyString_Check(%in)'> + <add-conversion type="PyString"> %out = %OUTTYPE(PyString_AS_STRING(%in), PyString_GET_SIZE(%in)); </add-conversion> </target-to-native> @@ -1811,7 +1871,7 @@ </inject-code> <conversion-rule class="target" file="date_conversions.h"> <target-to-native> - <add-conversion type='PyDate' check='PyDate_ImportAndCheck(%in)'> + <add-conversion type="PyDate" check="PyDate_ImportAndCheck(%in)"> int day = PyDateTime_GET_DAY(%in); int month = PyDateTime_GET_MONTH(%in); int year = PyDateTime_GET_YEAR(%in); |