diff options
-rw-r--r-- | generator/cppgenerator.cpp | 847 | ||||
-rw-r--r-- | generator/cppgenerator.h | 53 | ||||
-rw-r--r-- | generator/headergenerator.cpp | 83 | ||||
-rw-r--r-- | generator/shibokengenerator.cpp | 405 | ||||
-rw-r--r-- | generator/shibokengenerator.h | 31 | ||||
-rw-r--r-- | libshiboken/CMakeLists.txt | 2 | ||||
-rw-r--r-- | libshiboken/basewrapper.cpp | 42 | ||||
-rw-r--r-- | libshiboken/basewrapper.h | 21 | ||||
-rw-r--r-- | libshiboken/basewrapper_p.h | 6 | ||||
-rw-r--r-- | libshiboken/sbkconverter.cpp | 214 | ||||
-rw-r--r-- | libshiboken/sbkconverter.h | 205 | ||||
-rw-r--r-- | libshiboken/sbkconverter_p.h | 90 | ||||
-rw-r--r-- | libshiboken/shiboken.h | 1 | ||||
-rw-r--r-- | tests/samplebinding/injectcode_test.py | 13 | ||||
-rw-r--r-- | tests/samplebinding/typesystem_sample.xml | 63 |
15 files changed, 1728 insertions, 348 deletions
diff --git a/generator/cppgenerator.cpp b/generator/cppgenerator.cpp index 7142abc7..ec376d5f 100644 --- a/generator/cppgenerator.cpp +++ b/generator/cppgenerator.cpp @@ -526,6 +526,7 @@ void CppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *metaCl } s << endl; + writeConverterFunctions(s, metaClass); writeClassRegister(s, metaClass); // class inject-code native/end @@ -796,23 +797,44 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun s << INDENT << "bool invalidateArg0 = " PYTHON_RETURN_VAR "->ob_refcnt == 1;" << endl; if (func->type() && func->typeReplaced(0) != "PyObject") { - s << INDENT << "// Check return type" << endl; - s << INDENT << "bool typeIsValid = "; - writeTypeCheck(s, func->type(), PYTHON_RETURN_VAR, isNumber(func->type()->typeEntry()), func->typeReplaced(0)); - s << ';' << endl; - s << INDENT << "if (!typeIsValid"; - s << (isPointerToWrapperType(func->type()) ? " && " PYTHON_RETURN_VAR " != Py_None" : ""); - s << ") {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ - "\"Invalid return value in function %s, expected %s, got %s.\", \""; - s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func); - s << ", " PYTHON_RETURN_VAR "->ob_type->tp_name);" << endl; - s << INDENT << "return " << defaultReturnExpr << ';' << endl; - } - s << INDENT << '}' << endl; + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(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; + { + Indentation indent(INDENT); + s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ + "\"Invalid return value in function %s, expected %s, got %s.\", \""; + s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func); + s << ", " PYTHON_RETURN_VAR "->ob_type->tp_name);" << endl; + s << INDENT << "return " << defaultReturnExpr << ';' << endl; + } + s << INDENT << '}' << endl; + + } else { // TODO-CONVERTER -------------------------------------------------------------- + + s << INDENT << "// Check return type" << endl; + s << INDENT << "bool typeIsValid = "; + writeTypeCheck(s, func->type(), PYTHON_RETURN_VAR, + isNumber(func->type()->typeEntry()), func->typeReplaced(0)); + s << ';' << endl; + + s << INDENT << "if (!typeIsValid"; + s << (isPointerToWrapperType(func->type()) ? " && " PYTHON_RETURN_VAR " != Py_None" : ""); + s << ") {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ + "\"Invalid return value in function %s, expected %s, got %s.\", \""; + s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func); + s << ", " PYTHON_RETURN_VAR "->ob_type->tp_name);" << endl; + s << INDENT << "return " << defaultReturnExpr << ';' << endl; + } + s << INDENT << '}' << endl; + + } // TODO-CONVERTER --------------------------------------------------------------------- } if (!func->conversionRule(TypeSystem::NativeCode, 0).isEmpty()) { @@ -866,6 +888,12 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun s << '(' << typeCast << ')'; } } + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(type)) { + if (func->type()->isReference() && !isPointer(func->type())) + s << '*'; + } + // TODO-CONVERTER ----------------------------------------------------------------------- s << CPP_RETURN_VAR ";" << endl; } @@ -925,6 +953,231 @@ void CppGenerator::writeMetaCast(QTextStream& s, const AbstractMetaClass* metaCl s << "}" << endl << endl; } +void CppGenerator::writeConverterFunctions(QTextStream& s, const AbstractMetaClass* metaClass) +{ + if (metaClass->isNamespace()) + return; + s << "// Type conversion functions." << endl << endl; + + QString typeName = getFullTypeName(metaClass); + QString cpythonType = cpythonTypeName(metaClass); + + // Returns the C++ pointer of the Python wrapper. + s << "// Python to C++ pointer conversion - returns the C++ object of the Python wrapper (keeps object identity)." << endl; + + QString sourceTypeName = metaClass->name(); + QString targetTypeName = QString("%1_PTR").arg(metaClass->name()); + QString code; + QTextStream c(&code); + c << INDENT << "Shiboken::Conversions::pythonToCppPointer(&" << cpythonType << ", pyIn, cppOut);"; + writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); + + // "Is convertible" function for the Python object to C++ pointer conversion. + QString pyTypeCheck = QString("PyObject_TypeCheck(pyIn, (PyTypeObject*)&%1)").arg(cpythonType); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck, QString(), true); + s << endl; + + // C++ pointer to a Python wrapper, keeping identity. + s << "// C++ to Python pointer conversion - tries to find the Python wrapper for the C++ object (keeps object identity)." << endl; + code.clear(); + c << INDENT << "PyObject* pyOut = (PyObject*)Shiboken::BindingManager::instance().retrieveWrapper(cppIn);" << endl; + c << INDENT << "if (pyOut) {" << endl; + { + Indentation indent(INDENT); + c << INDENT << "Py_INCREF(pyOut);" << endl; + c << INDENT << "return pyOut;" << endl; + } + c << INDENT << '}' << endl; + c << INDENT << "const char* typeName = typeid(*((" << typeName << "*)cppIn)).name();" << endl; + c << INDENT << "return Shiboken::Object::newObject(&" << cpythonType; + c << ", const_cast<void*>(cppIn), false, false, typeName);"; + std::swap(targetTypeName, sourceTypeName); + writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName); + + // The conversions for an Object Type end here. + if (!metaClass->typeEntry()->isValue()) { + s << endl; + return; + } + + // Always copies C++ value (not pointer, and not reference) to a new Python wrapper. + s << endl << "// C++ to Python copy conversion." << endl; + sourceTypeName = QString("%1_COPY").arg(metaClass->name()); + targetTypeName = metaClass->name(); + code.clear(); + c << INDENT << "return Shiboken::Object::newObject(&" << cpythonType << ", new ::" << wrapperName(metaClass); + c << "(*((" << typeName << "*)cppIn)), true, true);"; + writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName); + s << endl; + + // Python to C++ copy conversion. + s << "// Python to C++ copy conversion." << endl; + sourceTypeName = metaClass->name(); + targetTypeName = QString("%1_COPY").arg(sourceTypeName); + code.clear(); + c << INDENT << "*((" << typeName << "*)cppOut) = *" << cpythonWrapperCPtr(metaClass->typeEntry(), "pyIn") << ';'; + writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); + + // "Is convertible" function for the Python object to C++ value copy conversion. + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck); + s << endl; + + // User provided implicit conversions. + CustomConversion* customConversion = metaClass->typeEntry()->customConversion(); + + // Implicit conversions. + AbstractMetaFunctionList implicitConvs; + if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) { + foreach (AbstractMetaFunction* func, implicitConversions(metaClass->typeEntry())) { + if (!func->isUserAdded()) + implicitConvs << func; + } + } + + if (!implicitConvs.isEmpty()) + s << "// Implicit conversions." << endl; + + AbstractMetaType* targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass); + foreach (const AbstractMetaFunction* conv, implicitConvs) { + if (conv->isModifiedRemoved()) + continue; + + QString typeCheck; + QString toCppConv; + if (conv->isConversionOperator()) { + const AbstractMetaClass* sourceClass = conv->ownerClass(); + typeCheck = QString("PyObject_TypeCheck(pyIn, %1)").arg(cpythonTypeNameExt(sourceClass->typeEntry())); + toCppConv = QString("*%1").arg(cpythonWrapperCPtr(sourceClass->typeEntry(), "pyIn")); + + } else { + // Constructor that does implicit conversion. + if (!conv->typeReplaced(1).isEmpty()) + 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)); + if (isWrapperType(sourceType)) { + toCppConv = QString("%1%2") + .arg((sourceType->isReference() || !isPointerToWrapperType(sourceType)) ? "*" : "") + .arg(cpythonWrapperCPtr(sourceType->typeEntry(), "pyIn")); + } else { + QTextStream tcc(&toCppConv); + writeToCppConversion(tcc, sourceType, metaClass, "pyIn", "/*BOZO-1043*/"); + } + } + const AbstractMetaType* sourceType = conv->isConversionOperator() + ? buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass()) + : conv->arguments().first()->type(); + writePythonToCppConversionFunctions(s, sourceType, targetType, typeCheck, toCppConv); + } + + writeCustomConverterFunctions(s, customConversion); +} + +void CppGenerator::writeCustomConverterFunctions(QTextStream& s, const CustomConversion* customConversion) +{ + if (!customConversion) + return; + const CustomConversion::TargetToNativeConversions& toCppConversions = customConversion->targetToNativeConversions(); + if (toCppConversions.isEmpty()) + return; + s << "// Python to C++ conversions for type '" << customConversion->ownerType()->qualifiedCppName() << "'." << endl; + foreach (CustomConversion::TargetToNativeConversion* toNative, toCppConversions) + writePythonToCppConversionFunctions(s, toNative, customConversion->ownerType()); + s << endl; +} + +void CppGenerator::writeConverterRegister(QTextStream& s, const AbstractMetaClass* metaClass) +{ + if (metaClass->isNamespace()) + return; + s << INDENT << "// Register Converter" << endl; + s << INDENT; + if (!isObjectType(metaClass)) + s << "SbkConverter* converter = "; + s << "Shiboken::Conversions::createConverter(&" << cpythonTypeName(metaClass) << ',' << endl; + { + Indentation indent(INDENT); + QString sourceTypeName = metaClass->name(); + QString targetTypeName = QString("%1_PTR").arg(metaClass->name()); + s << INDENT << pythonToCppFunctionName(sourceTypeName, targetTypeName) << ',' << endl; + s << INDENT << convertibleToCppFunctionName(sourceTypeName, targetTypeName) << ',' << endl; + std::swap(targetTypeName, sourceTypeName); + s << INDENT << cppToPythonFunctionName(sourceTypeName, targetTypeName); + if (metaClass->typeEntry()->isValue()) { + s << ',' << endl; + sourceTypeName = QString("%1_COPY").arg(metaClass->name()); + s << INDENT << cppToPythonFunctionName(sourceTypeName, targetTypeName); + } + } + s << ");" << endl; + + if (!metaClass->typeEntry()->isValue()) + return; + + // Python to C++ copy (value, not pointer neither reference) conversion. + s << INDENT << "// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter." << endl; + QString sourceTypeName = metaClass->name(); + QString targetTypeName = QString("%1_COPY").arg(metaClass->name()); + QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); + QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); + writeAddPythonToCppConversion(s, "converter", toCpp, isConv); + + // User provided implicit conversions. + CustomConversion* customConversion = metaClass->typeEntry()->customConversion(); + + // Add implicit conversions. + AbstractMetaFunctionList implicitConvs; + if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) { + foreach (AbstractMetaFunction* func, implicitConversions(metaClass->typeEntry())) { + if (!func->isUserAdded()) + implicitConvs << func; + } + } + + if (!implicitConvs.isEmpty()) + s << INDENT << "// Add implicit conversions to type converter." << endl; + + AbstractMetaType* targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass); + foreach (const AbstractMetaFunction* conv, implicitConvs) { + if (conv->isModifiedRemoved()) + continue; + const AbstractMetaType* sourceType; + if (conv->isConversionOperator()) { + sourceType = buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass()); + } else { + // Constructor that does implicit conversion. + if (!conv->typeReplaced(1).isEmpty()) + continue; + sourceType = conv->arguments().first()->type(); + } + QString toCpp = pythonToCppFunctionName(sourceType, targetType); + QString isConv = convertibleToCppFunctionName(sourceType, targetType); + writeAddPythonToCppConversion(s, "converter", toCpp, isConv); + } + + writeCustomConverterRegister(s, customConversion, "converter"); +} + +void CppGenerator::writeCustomConverterRegister(QTextStream& s, const CustomConversion* customConversion, const QString& converterVar) +{ + if (!customConversion) + return; + const CustomConversion::TargetToNativeConversions& toCppConversions = customConversion->targetToNativeConversions(); + if (toCppConversions.isEmpty()) + return; + s << INDENT << "// Add user defined implicit conversions to type converter." << endl; + foreach (CustomConversion::TargetToNativeConversion* toNative, toCppConversions) { + QString toCpp = pythonToCppFunctionName(toNative, customConversion->ownerType()); + QString isConv = convertibleToCppFunctionName(toNative, customConversion->ownerType()); + writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); + } +} + void CppGenerator::writeMethodWrapperPreamble(QTextStream& s, OverloadData& overloadData) { const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); @@ -938,7 +1191,8 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream& s, OverloadData& over if (rfunc->isConstructor()) { // Check if the right constructor was called. if (!ownerClass->hasPrivateDestructor()) { - s << INDENT << "if (Shiboken::Object::isUserType(" PYTHON_SELF_VAR ") && !Shiboken::ObjectType::canCallConstructor(" PYTHON_SELF_VAR "->ob_type, Shiboken::SbkType< ::"; + s << INDENT; + s << "if (Shiboken::Object::isUserType(" PYTHON_SELF_VAR ") && !Shiboken::ObjectType::canCallConstructor(" PYTHON_SELF_VAR "->ob_type, Shiboken::SbkType< ::"; s << ownerClass->qualifiedCppName() << " >()))" << endl; Indentation indent(INDENT); s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; @@ -963,14 +1217,24 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream& s, OverloadData& over usesNamedArguments = rfunc->isCallOperator() || overloadData.hasArgumentWithDefaultValue(); } - if (maxArgs > 0) + if (maxArgs > 0) { s << INDENT << "int overloadId = -1;" << endl; + s << INDENT << "PythonToCppFunc " PYTHON_TO_CPP_VAR; + if (pythonFunctionWrapperUsesListOfArguments(overloadData)) + s << "[] = { 0" << QString(", 0").repeated(maxArgs-1) << " }"; + s << ';' << endl; + // TODO-CONVERTER: remove this later. + // Make a check that ask something like: "there's no conversions in the overloads?". + writeUnusedVariableCast(s, PYTHON_TO_CPP_VAR); + // TODO-CONVERTER: RANDOM THOUGHT: I gave that decisor some overloads. Decisors love overloads. + } if (usesNamedArguments && !rfunc->isCallOperator()) s << INDENT << "int numNamedArgs = (kwds ? PyDict_Size(kwds) : 0);" << endl; if (initPythonArguments) { s << INDENT << "int numArgs = "; + // TODO-CONVERTER: review rfunc->isConstructor() if (minArgs == 0 && maxArgs == 1 && !rfunc->isConstructor() && !pythonFunctionWrapperUsesListOfArguments(overloadData)) s << "(" PYTHON_ARG " == 0 ? 0 : 1);" << endl; else @@ -1368,13 +1632,13 @@ void CppGenerator::writeCppSelfDefinition(QTextStream& s, const AbstractMetaFunc if (func->isOperatorOverload() && func->isBinaryOperator()) { QString checkFunc = cpythonCheckFunction(func->ownerClass()->typeEntry()); - s << INDENT << "bool isReverse = " << checkFunc << "(" PYTHON_ARG ")" << endl; + s << INDENT << "bool isReverse = " << checkFunc << /*TODO-CONVERTER "("*/ PYTHON_ARG ")" << endl; { Indentation indent1(INDENT); Indentation indent2(INDENT); Indentation indent3(INDENT); Indentation indent4(INDENT); - s << INDENT << "&& !" << checkFunc << "(" PYTHON_SELF_VAR ");" << endl; + s << INDENT << "&& !" << checkFunc << /*TODO-CONVERTER "("*/ PYTHON_SELF_VAR ");" << endl; } s << INDENT << "if (isReverse)" << endl; Indentation indent(INDENT); @@ -1489,6 +1753,13 @@ void CppGenerator::writeInvalidPyObjectCheck(QTextStream& s, const QString& pyOb s << INDENT << "return " << m_currentErrorCode << ';' << endl; } +static QString pythonToCppConverterForArgumentName(const QString& argumentName) +{ + static QRegExp pyArgsRegex(PYTHON_ARGS"(\\[\\d+[-]?\\d*\\])"); + pyArgsRegex.indexIn(argumentName); + return QString(PYTHON_TO_CPP_VAR"%1").arg(pyArgsRegex.cap(1)); +} + void CppGenerator::writeTypeCheck(QTextStream& s, const AbstractMetaType* argType, QString argumentName, bool isNumber, QString customType, bool rejectNull) { QString customCheck; @@ -1506,6 +1777,12 @@ void CppGenerator::writeTypeCheck(QTextStream& s, const AbstractMetaType* argTyp typeCheck = customCheck; typeCheck.append(QString("(%1)").arg(argumentName)); + // TODO-CONVERTER ----------------------------------------------------------------------- + if (customCheck.isEmpty() && isWrapperType(argType)) { + typeCheck = QString("(%1 = %2))").arg(pythonToCppConverterForArgumentName(argumentName)).arg(typeCheck); + } + // TODO-CONVERTER ----------------------------------------------------------------------- + if (rejectNull) typeCheck = QString("(%1 != Py_None && %2)").arg(argumentName).arg(typeCheck); @@ -1581,25 +1858,82 @@ void CppGenerator::writePythonToCppTypeConversion(QTextStream& s, const AbstractMetaClass* context, const QString& defaultValue) { + // TODO-CONVERTER - is this needed here? If so, at least raise a generator warning. if (type->typeEntry()->isCustom() || type->typeEntry()->isVarargs()) return; + QString cppOutAux = QString("%1_local").arg(cppOut); + + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(type)) { + + bool treatAsPointer = isValueTypeWithCopyConstructorOnly(type); + bool isPointerOrObjectType = isObjectType(type) || isPointer(type); + bool mayHaveImplicitConversion = type->isReference() && !(treatAsPointer || isPointerOrObjectType); + QString typeName = getFullTypeNameWithoutModifiers(type); + if (mayHaveImplicitConversion) { + s << INDENT << typeName << ' ' << cppOutAux << " = "; + writeMinimalConstructorExpression(s, type, defaultValue); + s << ';' << endl; + } + + s << INDENT << typeName; + if (treatAsPointer || isPointerOrObjectType) { + s << "* " << cppOut << (defaultValue.isEmpty() ? "" : QString(" = %1").arg(defaultValue)); + } else if (type->isReference()) { + s << "* " << cppOut << " = &" << cppOutAux; + } else { + s << ' ' << cppOut << " = "; + writeMinimalConstructorExpression(s, type, defaultValue); + } + s << ';' << endl; + + QString pythonToCppFunc = pythonToCppConverterForArgumentName(pyIn); + + s << INDENT; + if (!defaultValue.isEmpty()) + s << "if (" << pythonToCppFunc << ") "; + + QString pythonToCppCall = QString("%1(%2, &%3)").arg(pythonToCppFunc).arg(pyIn).arg(cppOut); + if (!mayHaveImplicitConversion) { + s << pythonToCppCall << ';' << endl; + return; + } + + if (!defaultValue.isEmpty()) + s << '{' << endl << INDENT; + + s << "if (Shiboken::Conversions::isImplicitConversion((SbkObjectType*)"; + s << cpythonTypeNameExt(type) << ", " << pythonToCppFunc << "))" << endl; + { + Indentation indent(INDENT); + s << INDENT << pythonToCppFunc << '(' << pyIn << ", &" << cppOutAux << ");" << endl; + } + s << INDENT << "else" << endl; + { + Indentation indent(INDENT); + s << INDENT << pythonToCppCall << ';' << endl; + } + + if (!defaultValue.isEmpty()) + s << INDENT << '}'; + s << endl; + return; + } + // TODO-CONVERTER ----------------------------------------------------------------------- + QString conversion; QTextStream c(&conversion); - writeToCppConversion(c, type, context, pyIn); - QString typeName; - QString cppOutAux = QString("%1_tmp").arg(cppOut); + writeToCppConversion(c, type, context, pyIn, "/*BOZO-1906*/"); // Value type that has default value. if (type->isValue() && !defaultValue.isEmpty()) s << INDENT << type->typeEntry()->name() << ' ' << cppOutAux << " = " << defaultValue << ';' << endl; - if (typeName.isEmpty()) { - // exclude const on Objects - Options flags = getConverterOptions(type); - typeName = translateTypeForWrapperMethod(type, context, flags).trimmed(); - } + // exclude const on Objects + Options flags = getConverterOptions(type); + QString typeName = translateTypeForWrapperMethod(type, context, flags).trimmed(); if (!defaultValue.isEmpty()) { conversion.prepend(QString("%1 ? ").arg(pyIn)); @@ -1868,7 +2202,6 @@ void CppGenerator::writeSingleFunctionCall(QTextStream& s, const OverloadData& o return; } - const AbstractMetaClass* implementingClass = overloadData.referenceFunction()->implementingClass(); bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); // Handle named arguments. @@ -1905,72 +2238,230 @@ void CppGenerator::writeSingleFunctionCall(QTextStream& s, const OverloadData& o QString argName = QString(CPP_ARG"%1").arg(argPos); QString pyArgName = usePyArgs ? QString(PYTHON_ARGS "[%1]").arg(argPos) : PYTHON_ARG; QString defaultValue = guessScopeForDefaultValue(func, arg); - writeArgumentConversion(s, argType, argName, pyArgName, implementingClass, defaultValue, func->isUserAdded()); + writeArgumentConversion(s, argType, argName, pyArgName, func->implementingClass(), defaultValue, func->isUserAdded()); } s << endl; int numRemovedArgs = OverloadData::numberOfRemovedArguments(func); - s << INDENT << "if(!PyErr_Occurred()) {" << endl; + s << INDENT << "if (!PyErr_Occurred()) {" << endl; { Indentation indentation(INDENT); writeMethodCall(s, func, func->arguments().size() - numRemovedArgs); if (!func->isConstructor()) writeNoneReturn(s, func, overloadData.hasNonVoidReturnType()); } - s << INDENT << "}" << endl; + s << INDENT << '}' << endl; +} + +QString CppGenerator::cppToPythonFunctionName(const QString& sourceTypeName, QString targetTypeName) +{ + if (targetTypeName.isEmpty()) + targetTypeName = sourceTypeName; + return QString("%1_CppToPython_%2").arg(sourceTypeName).arg(targetTypeName); +} + +QString CppGenerator::pythonToCppFunctionName(const QString& sourceTypeName, const QString& targetTypeName) +{ + return QString("%1_PythonToCpp_%2").arg(sourceTypeName).arg(targetTypeName); +} +QString CppGenerator::pythonToCppFunctionName(const AbstractMetaType* sourceType, const AbstractMetaType* targetType) +{ + return pythonToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType)); +} +QString CppGenerator::pythonToCppFunctionName(const CustomConversion::TargetToNativeConversion* toNative, + const TypeEntry* targetType) +{ + return pythonToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType)); +} + +QString CppGenerator::convertibleToCppFunctionName(const QString& sourceTypeName, const QString& targetTypeName) +{ + return QString("is_%1_PythonToCpp_%2_Convertible").arg(sourceTypeName).arg(targetTypeName); +} +QString CppGenerator::convertibleToCppFunctionName(const AbstractMetaType* sourceType, const AbstractMetaType* targetType) +{ + return convertibleToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType)); +} +QString CppGenerator::convertibleToCppFunctionName(const CustomConversion::TargetToNativeConversion* toNative, + const TypeEntry* targetType) +{ + return convertibleToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType)); +} + +void CppGenerator::writeCppToPythonFunction(QTextStream& s, const QString& code, const QString& sourceTypeName, QString targetTypeName) +{ + QString prettyCode; + QTextStream c(&prettyCode); + formatCode(c, code, INDENT); + + s << "static PyObject* " << cppToPythonFunctionName(sourceTypeName, targetTypeName); + s << "(const void* cppIn) {" << endl; + s << prettyCode; + s << '}' << endl; +} +void CppGenerator::writeCppToPythonFunction(QTextStream& s, const CustomConversion* customConversion) +{ + QString code = customConversion->nativeToTargetConversion(); + code.prepend(QString("::%1& cppInRef = *((::%1*)cppIn);\n").arg(customConversion->ownerType()->qualifiedCppName())); + code.replace("%INTYPE", cpythonTypeNameExt(customConversion->ownerType())); + code.replace("%OUTTYPE", "PyObject*"); + code.replace("%in", "cppInRef"); + code.replace("%out", "pyOut"); + writeCppToPythonFunction(s, code, fixedCppTypeName(customConversion->ownerType())); +} + +void CppGenerator::writePythonToCppFunction(QTextStream& s, const QString& code, const QString& sourceTypeName, const QString& targetTypeName) +{ + QString prettyCode; + QTextStream c(&prettyCode); + formatCode(c, code, INDENT); + s << "static void " << pythonToCppFunctionName(sourceTypeName, targetTypeName); + s << "(PyObject* pyIn, void* cppOut) {" << endl; + s << prettyCode; + s << '}' << endl; +} + +void CppGenerator::writeIsPythonConvertibleToCppFunction(QTextStream& s, + const QString& sourceTypeName, + const QString& targetTypeName, + const QString& condition, + QString pythonToCppFuncName, + bool acceptNoneAsCppNull) +{ + if (pythonToCppFuncName.isEmpty()) + pythonToCppFuncName = pythonToCppFunctionName(sourceTypeName, targetTypeName); + + s << "static PythonToCppFunc " << convertibleToCppFunctionName(sourceTypeName, targetTypeName); + s << "(PyObject* pyIn) {" << endl; + if (acceptNoneAsCppNull) { + s << INDENT << "if (pyIn == Py_None)" << endl; + Indentation indent(INDENT); + s << INDENT << "return Shiboken::Conversions::nonePythonToCppNullPtr;" << endl; + } + s << INDENT << "if (" << condition << ')' << endl; + { + Indentation indent(INDENT); + s << INDENT << "return " << pythonToCppFuncName << ';' << endl; + } + s << INDENT << "return 0;" << endl; + s << '}' << endl; +} + +void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, + const AbstractMetaType* sourceType, + const AbstractMetaType* targetType, + QString typeCheck, + QString conversion) +{ + QString sourcePyType = cpythonTypeNameExt(sourceType); + + // Python to C++ conversion function. + QString code; + QTextStream c(&code); + if (conversion.isEmpty()) + conversion = QString("*%1").arg(cpythonWrapperCPtr(sourceType->typeEntry(), "pyIn")); + c << INDENT << QString("*((::%1*)cppOut) = ::%1(%2);") + .arg(targetType->typeEntry()->qualifiedCppName()) + .arg(conversion); + QString sourceTypeName = fixedCppTypeName(sourceType); + QString targetTypeName = fixedCppTypeName(targetType); + writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); + + // Python to C++ convertible check function. + if (typeCheck.isEmpty()) + typeCheck = QString("PyObject_TypeCheck(pyIn, %1)").arg(sourcePyType); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); + s << endl; +} + +void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, + const CustomConversion::TargetToNativeConversion* toNative, + const TypeEntry* targetType) +{ + // Python to C++ conversion function. + QString code = toNative->conversion(); + QString inType; + if (toNative->sourceType()) + inType = cpythonTypeNameExt(toNative->sourceType()); + else + inType = QString("(&%1_Type)").arg(toNative->sourceTypeName()); + code.replace("%INTYPE", inType); + code.replace("%OUTTYPE", targetType->qualifiedCppName()); + code.replace("%in", "pyIn"); + code.replace("%out", QString("*((::%1*)cppOut)").arg(targetType->qualifiedCppName())); + + QString sourceTypeName = fixedCppTypeName(toNative); + QString targetTypeName = fixedCppTypeName(targetType); + writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); + + // Python to C++ convertible check function. + QString typeCheck = toNative->sourceTypeCheck(); + 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; + } + typeCheck = QString("PyObject_TypeCheck(%in, %1)").arg(cpythonTypeNameExt(toNative->sourceType())); + } + typeCheck.replace("%in", "pyIn"); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); +} + +void CppGenerator::writeAddPythonToCppConversion(QTextStream& s, const QString& converterVar, const QString& pythonToCppFunc, const QString& isConvertibleFunc) +{ + s << INDENT << "Shiboken::Conversions::addPythonToCppValueConversion(" << converterVar << ',' << endl; + { + Indentation indent(INDENT); + s << INDENT << pythonToCppFunc << ',' << endl; + s << INDENT << isConvertibleFunc; + } + s << ");" << endl; } void CppGenerator::writeNamedArgumentResolution(QTextStream& s, const AbstractMetaFunction* func, bool usePyArgs) { AbstractMetaArgumentList args = OverloadData::getArgumentsWithDefaultValues(func); - if (!args.isEmpty()) { - s << INDENT << "if (kwds) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "const char* errorArgName = 0;" << endl; - s << INDENT << "PyObject* "; - foreach (const AbstractMetaArgument* arg, args) { - int pyArgIndex = arg->argumentIndex() - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex()); - QString pyArgName = usePyArgs ? QString(PYTHON_ARGS "[%1]").arg(pyArgIndex) : PYTHON_ARG; - s << "value = PyDict_GetItemString(kwds, \"" << arg->name() << "\");" << endl; - s << INDENT << "if (value) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "if (" << pyArgName << ")" << endl; - { - Indentation indent(INDENT); - s << INDENT << "errorArgName = \"" << arg->name() << "\";" << endl; - } - s << INDENT << "else if ("; - writeTypeCheck(s, arg->type(), "value", isNumber(arg->type()->typeEntry())); - s << ')' << endl; - { - Indentation indent(INDENT); - s << INDENT << pyArgName << " = value;" << endl; - } - s << INDENT << "else" << endl; - { - Indentation indent(INDENT); - s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;" << endl; - } - } - s << INDENT << '}' << endl; - s << INDENT; - } - s << "if (errorArgName) {" << endl; + if (args.isEmpty()) + return; + + QString pyErrString("PyErr_SetString(PyExc_TypeError, \"" + fullPythonFunctionName(func) + + "(): got multiple values for keyword argument '%1'.\");"); + + s << INDENT << "if (kwds) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyObject* "; + foreach (const AbstractMetaArgument* arg, args) { + int pyArgIndex = arg->argumentIndex() - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex()); + QString pyArgName = usePyArgs ? QString(PYTHON_ARGS "[%1]").arg(pyArgIndex) : PYTHON_ARG; + s << "value = PyDict_GetItemString(kwds, \"" << arg->name() << "\");" << endl; + s << INDENT << "if (value && " << pyArgName << ") {" << endl; { Indentation indent(INDENT); - s << INDENT << "PyErr_Format(PyExc_TypeError, \"" << fullPythonFunctionName(func); - s << "(): got multiple values for keyword argument '%s'\", errorArgName);" << endl; + s << INDENT << pyErrString.arg(arg->name()) << endl; s << INDENT << "return " << m_currentErrorCode << ';' << endl; } + s << INDENT << "} else if (value) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << pyArgName << " = value;" << endl; + s << INDENT << "if (!"; + writeTypeCheck(s, arg->type(), pyArgName, isNumber(arg->type()->typeEntry())); + s << ')' << endl; + { + Indentation indent(INDENT); + s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;" << endl; + } + } s << INDENT << '}' << endl; - + if (arg != args.last()) + s << INDENT; } - s << INDENT << '}' << endl; } + s << INDENT << '}' << endl; } QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction* func, int argIndex, const AbstractMetaClass** wrappedClass) @@ -2075,9 +2566,11 @@ void CppGenerator::writeMethodCall(QTextStream& s, const AbstractMetaFunction* f userArgs << QString(CPP_ARG_REMOVED"%1").arg(i); } else { int idx = arg->argumentIndex() - removedArgs; + bool deRef = isValueTypeWithCopyConstructorOnly(arg->type()) + || (arg->type()->isReference() && isWrapperType(arg->type()) && !isPointer(arg->type())); QString argName = hasConversionRule ? QString("%1"CONV_RULE_OUT_VAR_SUFFIX).arg(arg->name()) - : QString(CPP_ARG"%1").arg(idx); + : QString("%1"CPP_ARG"%2").arg(deRef ? "*" : "").arg(idx); userArgs << argName; } } @@ -2110,15 +2603,14 @@ void CppGenerator::writeMethodCall(QTextStream& s, const AbstractMetaFunction* f bool isCtor = false; QString methodCall; QTextStream mc(&methodCall); - if (func->isOperatorOverload() && !func->isCallOperator()) { - QByteArray firstArg("(*" CPP_SELF_VAR ")"); + QString firstArg("(*" CPP_SELF_VAR ")"); if (func->isPointerOperator()) firstArg.remove(1, 1); // remove the de-reference operator - QByteArray secondArg(CPP_ARG0); + QString secondArg(CPP_ARG0); if (!func->isUnaryOperator() && shouldDereferenceArgumentPointer(func->arguments().first())) { - secondArg.prepend('('); + secondArg.prepend("(*"); secondArg.append(')'); } @@ -2147,7 +2639,7 @@ void CppGenerator::writeMethodCall(QTextStream& s, const AbstractMetaFunction* f QString className = wrapperName(func->ownerClass()); if (func->isCopyConstructor() && maxArgs == 1) { - mc << "new ::" << className << '(' << CPP_ARG0 << ')'; + mc << "new ::" << className << "(*" << CPP_ARG0 << ')'; } else { QString ctorCall = className + '(' + userArgs.join(", ") + ')'; if (usePySideExtensions() && func->ownerClass()->isQObject()) { @@ -2162,7 +2654,7 @@ void CppGenerator::writeMethodCall(QTextStream& s, const AbstractMetaFunction* f if (func->ownerClass()) { if (!avoidProtectedHack() || !func->isProtected()) { if (func->isStatic()) { - mc << func->ownerClass()->qualifiedCppName() << "::"; + mc << "::" << func->ownerClass()->qualifiedCppName() << "::"; } else { if (func->isConstant()) { if (avoidProtectedHack()) { @@ -2187,7 +2679,7 @@ void CppGenerator::writeMethodCall(QTextStream& s, const AbstractMetaFunction* f mc << func->originalName(); } else { if (!func->isStatic()) - mc << "((" << wrapperName(func->ownerClass()) << "*) " << CPP_SELF_VAR << ")->"; + mc << "((::" << wrapperName(func->ownerClass()) << "*) " << CPP_SELF_VAR << ")->"; if (!func->isAbstract()) mc << (func->isProtected() ? wrapperName(func->ownerClass()) : "::" + func->ownerClass()->qualifiedCppName()) << "::"; @@ -2292,17 +2784,16 @@ void CppGenerator::writeMethodCall(QTextStream& s, const AbstractMetaFunction* f if (arg_mod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::DefaultOwnership) continue; - s << INDENT; + s << INDENT << "Shiboken::Object::"; if (arg_mod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::TargetLangOwnership) { - s << "Shiboken::Object::getOwnership(" << pyArgName << ");"; + s << "getOwnership(" << pyArgName << ");"; } else if (wrappedClass->hasVirtualDestructor()) { - if (arg_mod.index == 0) { - s << "Shiboken::Object::releaseOwnership(" PYTHON_RETURN_VAR ");"; - } else { - s << "Shiboken::Object::releaseOwnership(" << pyArgName << ");"; - } + if (arg_mod.index == 0) + s << "releaseOwnership(" PYTHON_RETURN_VAR ");"; + else + s << "releaseOwnership(" << pyArgName << ");"; } else { - s << "Shiboken::Object::invalidate(" << pyArgName << ");"; + s << "invalidate(" << pyArgName << ");"; } s << endl; } @@ -2422,54 +2913,19 @@ void CppGenerator::writeSpecialCastFunction(QTextStream& s, const AbstractMetaCl s << "}\n\n"; } -void CppGenerator::writeExtendedIsConvertibleFunction(QTextStream& s, const TypeEntry* externalType, const QList<const AbstractMetaClass*>& conversions) -{ - s << "static bool " << extendedIsConvertibleFunctionName(externalType) << "(PyObject* pyobj)" << endl; - s << '{' << endl; - s << INDENT << "return "; - bool isFirst = true; - foreach (const AbstractMetaClass* metaClass, conversions) { - Indentation indent(INDENT); - if (isFirst) - isFirst = false; - else - s << endl << INDENT << " || "; - s << cpythonIsConvertibleFunction(metaClass->typeEntry()) << "(pyobj)"; - } - s << ';' << endl; - s << '}' << endl; -} - -void CppGenerator::writeExtendedToCppFunction(QTextStream& s, const TypeEntry* externalType, const QList<const AbstractMetaClass*>& conversions) -{ - s << "static void* " << extendedToCppFunctionName(externalType) << "(PyObject* pyobj)" << endl; - s << '{' << endl; - s << INDENT << "void* cptr = 0;" << endl; - bool isFirst = true; - foreach (const AbstractMetaClass* metaClass, conversions) { - s << INDENT; - if (isFirst) - isFirst = false; - else - s << "else "; - s << "if (" << cpythonIsConvertibleFunction(metaClass->typeEntry()) << "(pyobj))" << endl; - Indentation indent(INDENT); - s << INDENT << "cptr = new " << externalType->name() << '('; - writeToCppConversion(s, metaClass, "pyobj"); - s << ");" << endl; - } - s << INDENT << "return cptr;" << endl; - s << '}' << endl; -} - void CppGenerator::writeExtendedConverterInitialization(QTextStream& s, const TypeEntry* externalType, const QList<const AbstractMetaClass*>& conversions) { - s << INDENT << "// Extended implicit conversions for " << externalType->targetLangPackage() << '.' << externalType->name() << endl; - s << INDENT << "shiboType = reinterpret_cast<SbkObjectType*>("; - s << cppApiVariableName(externalType->targetLangPackage()) << '['; - s << getTypeIndexVariableName(externalType) << "]);" << endl; - s << INDENT << "Shiboken::ObjectType::setExternalIsConvertibleFunction(shiboType, " << extendedIsConvertibleFunctionName(externalType) << ");" << endl; - s << INDENT << "Shiboken::ObjectType::setExternalCppConversionFunction(shiboType, " << extendedToCppFunctionName(externalType) << ");" << endl; + s << INDENT << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << endl; + foreach (const AbstractMetaClass* sourceClass, conversions) { + QString converterVar = QString("(SbkObjectType*)%1[%2]") + .arg(cppApiVariableName(externalType->targetLangPackage())) + .arg(getTypeIndexVariableName(externalType)); + QString sourceTypeName = fixedCppTypeName(sourceClass->typeEntry()); + QString targetTypeName = fixedCppTypeName(externalType); + QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); + QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); + writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); + } } QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClass* metaClass) @@ -2879,7 +3335,7 @@ void CppGenerator::writeCopyFunction(QTextStream& s, const AbstractMetaClass* me s << "{" << endl; writeCppSelfDefinition(s, metaClass, false, true); s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << " = " << cpythonToPythonConversionFunction(metaClass); - s << "(" CPP_SELF_VAR ");" << endl; + s << CPP_SELF_VAR ");" << endl; writeFunctionReturnErrorCheckSection(s); s << INDENT << "return " PYTHON_RETURN_VAR ";" << endl; s << "}" << endl; @@ -2911,29 +3367,29 @@ void CppGenerator::writeGetterFunction(QTextStream& s, const AbstractMetaField* } } - s << INDENT << "PyObject* value = "; + s << INDENT << "PyObject* pyOut = "; if (newWrapperSameObject) { s << "Shiboken::Object::newObject((SbkObjectType*)" << cpythonTypeNameExt(fieldType); s << ", " << cppField << ", false, true);" << endl; - s << INDENT << "Shiboken::Object::setParent(" PYTHON_SELF_VAR ", value)"; + s << INDENT << "Shiboken::Object::setParent(" PYTHON_SELF_VAR ", pyOut)"; } else { writeToPythonConversion(s, fieldType, metaField->enclosingClass(), cppField); } s << ';' << endl; - s << INDENT << "return value;" << endl; + s << INDENT << "return pyOut;" << endl; s << '}' << endl; } void CppGenerator::writeSetterFunction(QTextStream& s, const AbstractMetaField* metaField) { ErrorCode errorCode(0); - s << "static int " << cpythonSetterFunctionName(metaField) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* value, void*)" << endl; + s << "static int " << cpythonSetterFunctionName(metaField) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* pyIn, void*)" << endl; s << '{' << endl; writeCppSelfDefinition(s, metaField->enclosingClass()); - s << INDENT << "if (value == 0) {" << endl; + s << INDENT << "if (pyIn == 0) {" << endl; { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_TypeError, \"'"; @@ -2944,8 +3400,14 @@ void CppGenerator::writeSetterFunction(QTextStream& s, const AbstractMetaField* AbstractMetaType* fieldType = metaField->type(); + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(fieldType)) { + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ';' << endl; + } + // TODO-CONVERTER ----------------------------------------------------------------------- + s << INDENT << "if (!"; - writeTypeCheck(s, fieldType, "value", isNumber(fieldType->typeEntry())); + writeTypeCheck(s, fieldType, "pyIn", isNumber(fieldType->typeEntry())); s << ") {" << endl; { Indentation indent(INDENT); @@ -2955,24 +3417,39 @@ void CppGenerator::writeSetterFunction(QTextStream& s, const AbstractMetaField* } s << INDENT << '}' << endl << endl; - QString conversion; - QTextStream c(&conversion); - writeToCppConversion(c, fieldType, metaField->enclosingClass(), "value"); - - if (avoidProtectedHack() && metaField->isProtected()) { - conversion = QString("((%1*)%2)->%3(%4)").arg(wrapperName(metaField->enclosingClass())) - .arg(CPP_SELF_VAR) - .arg(protectedFieldSetterName(metaField)) - .arg(conversion); - } else { - conversion = QString("%1->%2 = %3").arg(CPP_SELF_VAR).arg(metaField->name()).arg(conversion); + // TODO-CONVERTER ----------------------------------------------------------------------- + s << INDENT; + if (isWrapperType(fieldType)) { + if (avoidProtectedHack() && metaField->isProtected()) { + s << getFullTypeNameWithoutModifiers(fieldType); + s << (fieldType->indirections() == 1 ? "*" : "") << " cppOut;" << endl; + s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);" << endl; + s << INDENT << QString("((%1*)%2)->%3(cppOut)").arg(wrapperName(metaField->enclosingClass())) + .arg(CPP_SELF_VAR) + .arg(protectedFieldSetterName(metaField)); + } else { + s << PYTHON_TO_CPP_VAR << QString("(pyIn, &(%1->%2))").arg(CPP_SELF_VAR).arg(metaField->name()); + } + } else { // TODO-CONVERTER -------------------------------------------------------------- + QString conversion; + QTextStream c(&conversion); + writeToCppConversion(c, fieldType, metaField->enclosingClass(), "pyIn", QString()); + if (avoidProtectedHack() && metaField->isProtected()) { + conversion = QString("((%1*)%2)->%3(%4)").arg(wrapperName(metaField->enclosingClass())) + .arg(CPP_SELF_VAR) + .arg(protectedFieldSetterName(metaField)) + .arg(conversion); + } else { + conversion = QString("%1->%2 = %3").arg(CPP_SELF_VAR).arg(metaField->name()).arg(conversion); + } + s << conversion; } - - s << INDENT << conversion << ';' << endl << endl; + s << ';' << endl << endl; + // TODO-CONVERTER ----------------------------------------------------------------------- if (isPointerToWrapperType(fieldType)) { s << INDENT << "Shiboken::Object::keepReference(reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR "), \""; - s << metaField->name() << "\", value);" << endl; + s << metaField->name() << "\", pyIn);" << endl; } s << INDENT << "return 0;" << endl; @@ -2988,6 +3465,11 @@ void CppGenerator::writeRichCompareFunction(QTextStream& s, const AbstractMetaCl writeCppSelfDefinition(s, metaClass, false, true); writeUnusedVariableCast(s, CPP_SELF_VAR); s << INDENT << "PyObject* " PYTHON_RETURN_VAR " = 0;" << endl; + s << INDENT << "PythonToCppFunc " PYTHON_TO_CPP_VAR << ';' << endl; + // TODO-CONVERTER: remove this later. + // Make a check that ask something like: "there's no conversions in the overloads?". + writeUnusedVariableCast(s, PYTHON_TO_CPP_VAR); + // TODO-CONVERTER: RANDOM THOUGHT: I gave that decisor some overloads. Decisors love overloads. s << endl; s << INDENT << "switch (op) {" << endl; @@ -3013,21 +3495,22 @@ void CppGenerator::writeRichCompareFunction(QTextStream& s, const AbstractMetaCl bool first = true; OverloadData overloadData(overloads, this); - foreach (OverloadData* data, overloadData.nextOverloadData()) { - const AbstractMetaFunction* func = data->referenceFunction(); + foreach (OverloadData* od, overloadData.nextOverloadData()) { + const AbstractMetaFunction* func = od->referenceFunction(); if (func->isStatic()) continue; const AbstractMetaType* argType = getArgumentType(func, 1); if (!argType) continue; - bool numberType = alternativeNumericTypes == 1 || ShibokenGenerator::isPyInt(argType); if (!first) { s << " else "; } else { first = false; s << INDENT; } - s << "if (" << cpythonIsConvertibleFunction(argType, numberType) << "(" PYTHON_ARG ")) {" << endl; + s << "if ("; + writeTypeCheck(s, argType, PYTHON_ARG, alternativeNumericTypes == 1 || isPyInt(argType)); + s << ") {" << endl; { Indentation indent(INDENT); s << INDENT << "// " << func->signature() << endl; @@ -3038,9 +3521,10 @@ void CppGenerator::writeRichCompareFunction(QTextStream& s, const AbstractMetaCl CodeSnipList snips = func->injectedCodeSnips(); writeCodeSnips(s, snips, CodeSnip::Any, TypeSystem::TargetLangCode, func, func->arguments().last()); } else { - QString expression = QString("%1%2 %3 " CPP_ARG0) + QString expression = QString("%1%2 %3 (%4" CPP_ARG0 ")") .arg(func->isPointerOperator() ? "&" : "") - .arg(CPP_SELF_VAR).arg(op); + .arg(CPP_SELF_VAR).arg(op) + .arg(shouldDereferenceAbstractMetaTypePointer(argType) ? "*" : ""); s << INDENT; if (func->type()) s << func->type()->cppSignature() << " " CPP_RETURN_VAR " = "; @@ -3454,7 +3938,7 @@ void CppGenerator::writeClassRegister(QTextStream& s, const AbstractMetaClass* m s << INDENT << "if (!Shiboken::ObjectType::introduceWrapperType(" << enclosingObjectVariable; s << ", \"" << metaClass->name() << "\", \""; // Original name - s << metaClass->qualifiedCppName() << (ShibokenGenerator::isObjectType(classTypeEntry) ? "*" : ""); + s << metaClass->qualifiedCppName() << (isObjectType(classTypeEntry) ? "*" : ""); s << "\"," << endl; { Indentation indent(INDENT); @@ -3488,6 +3972,10 @@ void CppGenerator::writeClassRegister(QTextStream& s, const AbstractMetaClass* m } s << INDENT << '}' << endl << endl; + // Register conversions for the type. + writeConverterRegister(s, metaClass); + s << endl; + // class inject-code target/beginning if (!classTypeEntry->codeSnips().isEmpty()) { writeCodeSnips(s, classTypeEntry->codeSnips(), CodeSnip::Beginning, TypeSystem::TargetLangCode, metaClass); @@ -3820,6 +4308,9 @@ void CppGenerator::finishGeneration() s << inc; s << endl; + s << "// Current module's type array." << endl; + s << "PyTypeObject** " << cppApiVariableName() << ';' << endl; + CodeSnipList snips; if (moduleEntry) snips = moduleEntry->codeSnips(); @@ -3880,24 +4371,30 @@ void CppGenerator::finishGeneration() } } - s << "// Current module's type array." << endl; - s << "PyTypeObject** " << cppApiVariableName() << ';' << endl; - s << "// Required modules' type arrays." << endl; - foreach (const QString& requiredModule, typeDb->requiredTargetImports()) + QStringList requiredModules = typeDb->requiredTargetImports(); + if (!requiredModules.isEmpty()) + s << "// Required modules' type and converter arrays." << endl; + foreach (const QString& requiredModule, requiredModules) s << "PyTypeObject** " << cppApiVariableName(requiredModule) << ';' << endl; s << endl; s << "// Module initialization "; s << "------------------------------------------------------------" << endl; ExtendedConverterData extendedConverters = getExtendedConverters(); - if (!extendedConverters.isEmpty()) - s << "// Extended Converters" << endl; - foreach (const TypeEntry* externalType, extendedConverters.keys()) { - writeExtendedIsConvertibleFunction(s, externalType, extendedConverters[externalType]); - writeExtendedToCppFunction(s, externalType, extendedConverters[externalType]); + if (!extendedConverters.isEmpty()) { + s << endl << "// Extended Converters." << endl << endl; + foreach (const TypeEntry* externalType, extendedConverters.keys()) { + s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << endl; + foreach (const AbstractMetaClass* sourceClass, extendedConverters[externalType]) { + AbstractMetaType* sourceType = buildAbstractMetaTypeFromAbstractMetaClass(sourceClass); + AbstractMetaType* targetType = buildAbstractMetaTypeFromTypeEntry(externalType); + writePythonToCppConversionFunctions(s, sourceType, targetType); + delete sourceType; + delete targetType; + } + } s << endl; } - s << endl; s << "#if defined _WIN32 || defined __CYGWIN__" << endl; @@ -3945,9 +4442,9 @@ void CppGenerator::finishGeneration() s << INDENT << "}" << endl << endl; } - s << INDENT << "// Create an array of wrapper types for the current module." << endl; int maxTypeIndex = getMaxTypeIndex(); if (maxTypeIndex) { + s << INDENT << "// Create an array of wrapper types for the current module." << endl; s << INDENT << "static PyTypeObject* cppApi[SBK_" << moduleName() << "_IDX_COUNT];" << endl; s << INDENT << cppApiVariableName() << " = cppApi;" << endl << endl; } @@ -3962,15 +4459,11 @@ void CppGenerator::finishGeneration() s << INDENT << "// Initialize classes in the type system" << endl; s << classPythonDefines; - if (!extendedConverters.isEmpty()) { - s << INDENT << "// Initialize extended Converters" << endl; - s << INDENT << "SbkObjectType* shiboType;" << endl << endl; - } + s << endl; foreach (const TypeEntry* externalType, extendedConverters.keys()) { writeExtendedConverterInitialization(s, externalType, extendedConverters[externalType]); s << endl; } - s << endl; writeEnumsInitialization(s, globalEnums); diff --git a/generator/cppgenerator.h b/generator/cppgenerator.h index 09486428..f4ff188d 100644 --- a/generator/cppgenerator.h +++ b/generator/cppgenerator.h @@ -51,6 +51,11 @@ private: void writeMetaObjectMethod(QTextStream& s, const AbstractMetaClass* metaClass); void writeMetaCast(QTextStream& s, const AbstractMetaClass* metaClass); + void writeConverterFunctions(QTextStream& s, const AbstractMetaClass* metaClass); + void writeCustomConverterFunctions(QTextStream& s, const CustomConversion* customConversion); + void writeConverterRegister(QTextStream& s, const AbstractMetaClass* metaClass); + void writeCustomConverterRegister(QTextStream& s, const CustomConversion* customConversion, const QString& converterVar); + void writeMethodWrapperPreamble(QTextStream& s, OverloadData& overloadData); void writeConstructorWrapper(QTextStream& s, const AbstractMetaFunctionList overloads); void writeDestructorWrapper(QTextStream& s, const AbstractMetaClass* metaClass); @@ -141,7 +146,49 @@ private: void writeFunctionCalls(QTextStream& s, const OverloadData& overloadData); /// Writes the call to a single function usually from a collection of overloads. - void writeSingleFunctionCall(QTextStream& s, const OverloadData& overloadData, const AbstractMetaFunction* func = 0); + void writeSingleFunctionCall(QTextStream& s, const OverloadData& overloadData, const AbstractMetaFunction* func); + + /// Returns the name of a C++ to Python conversion function. + static QString cppToPythonFunctionName(const QString& sourceTypeName, QString targetTypeName = QString()); + + /// Returns the name of a Python to C++ conversion function. + static QString pythonToCppFunctionName(const QString& sourceTypeName, const QString& targetTypeName); + static QString pythonToCppFunctionName(const AbstractMetaType* sourceType, const AbstractMetaType* targetType); + static QString pythonToCppFunctionName(const CustomConversion::TargetToNativeConversion* toNative, const TypeEntry* targetType); + + /// Returns the name of a Python to C++ convertible check function. + static QString convertibleToCppFunctionName(const QString& sourceTypeName, const QString& targetTypeName); + static QString convertibleToCppFunctionName(const AbstractMetaType* sourceType, const AbstractMetaType* targetType); + static QString convertibleToCppFunctionName(const CustomConversion::TargetToNativeConversion* toNative, const TypeEntry* targetType); + + /// Writes a C++ to Python conversion function. + void writeCppToPythonFunction(QTextStream& s, const QString& code, const QString& sourceTypeName, QString targetTypeName = QString()); + void writeCppToPythonFunction(QTextStream& s, const CustomConversion* customConversion); + + /// Writes a Python to C++ conversion function. + void writePythonToCppFunction(QTextStream& s, const QString& code, const QString& sourceTypeName, const QString& targetTypeName); + + /// Writes a Python to C++ convertible check function. + void writeIsPythonConvertibleToCppFunction(QTextStream& s, + const QString& sourceTypeName, + const QString& targetTypeName, + const QString& condition, + QString pythonToCppFuncName = QString(), + bool acceptNoneAsCppNull = false); + + /// Writes a pair of Python to C++ conversion and check functions. + void writePythonToCppConversionFunctions(QTextStream& s, + const AbstractMetaType* sourceType, + const AbstractMetaType* targetType, + QString typeCheck = QString(), + QString conversion = QString()); + /// Writes a pair of Python to C++ conversion and check functions for implicit conversions. + void writePythonToCppConversionFunctions(QTextStream& s, + const CustomConversion::TargetToNativeConversion* toNative, + const TypeEntry* targetType); + + void writeAddPythonToCppConversion(QTextStream& s, const QString& converterVar, const QString& pythonToCppFunc, const QString& isConvertibleFunc); + void writeNamedArgumentResolution(QTextStream& s, const AbstractMetaFunction* func, bool usePyArgs); /// Returns a string containing the name of an argument for the given function and argument index. @@ -157,7 +204,7 @@ private: void writeSequenceMethods(QTextStream& s, const AbstractMetaClass* metaClass); void writeTypeAsSequenceDefinition(QTextStream& s, const AbstractMetaClass* metaClass); - /// Writes the struct PyMappingMethods for types thats supports the python mapping protocol + /// Writes the PyMappingMethods structure for types that supports the python mapping protocol. void writeTypeAsMappingDefinition(QTextStream& s, const AbstractMetaClass* metaClass); void writeMappingMethods(QTextStream& s, const AbstractMetaClass* metaClass); @@ -193,8 +240,6 @@ 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 writeExtendedIsConvertibleFunction(QTextStream& s, const TypeEntry* externalType, const QList<const AbstractMetaClass*>& conversions); - void writeExtendedToCppFunction(QTextStream& s, const TypeEntry* externalType, const QList<const AbstractMetaClass*>& conversions); 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 39d3eb4f..0de8c44e 100644 --- a/generator/headergenerator.cpp +++ b/generator/headergenerator.cpp @@ -564,82 +564,22 @@ void HeaderGenerator::writeTypeConverterImpl(QTextStream& s, const TypeEntry* ty const QString typeName = "::" + type->qualifiedCppName(); // Write Converter<T>::isConvertible - s << "inline bool Shiboken::Converter< " << typeName << " >::isConvertible(PyObject* pyobj)" << endl; + s << "inline bool Shiboken::Converter< " << typeName << " >::isConvertible(PyObject* pyIn)" << endl; s << '{' << endl; - - if (type->isValue()) { - s << INDENT << "if (ValueTypeConverter< " << typeName << " >::isConvertible(pyobj))" << endl; - Indentation indent(INDENT); - s << INDENT << "return true;" << endl; - } - - - s << INDENT << "SbkObjectType* shiboType = reinterpret_cast<SbkObjectType*>(SbkType< "; - s << typeName << " >());" << endl; - s << INDENT << "return "; - bool isFirst = true; - foreach (const AbstractMetaFunction* ctor, implicitConvs) { - Indentation indent(INDENT); - if (isFirst) - isFirst = false; - else - s << endl << INDENT << " || "; - if (ctor->isConversionOperator()) - s << cpythonCheckFunction(ctor->ownerClass()->typeEntry()); - else - s << cpythonCheckFunction(ctor->arguments().first()->type()); - s << "(pyobj)"; - } - s << endl; - { - Indentation indent(INDENT); - s << INDENT << " || (ObjectType::isExternalConvertible(shiboType, pyobj));" << endl; - } + s << INDENT << "return (bool)Shiboken::Conversions::isPythonToCppValueConvertible((SbkObjectType*)"; + s << cpythonTypeNameExt(type) << ", pyIn);" << endl; s << '}' << endl << endl; // Write Converter<T>::toCpp function - s << "inline " << typeName << " Shiboken::Converter< " << typeName << " >::toCpp(PyObject* pyobj)" << endl; + s << "inline " << typeName << " Shiboken::Converter< " << typeName << " >::toCpp(PyObject* pyIn)" << endl; s << '{' << endl; - - s << INDENT << "if (PyObject_TypeCheck(pyobj, SbkType< " << typeName << " >()))" << endl; - { - Indentation indent(INDENT); - s << INDENT << "return *" << cpythonWrapperCPtr(type, "pyobj") << ';' << endl; - } - - foreach (const AbstractMetaFunction* ctor, implicitConvs) { - if (ctor->isModifiedRemoved()) - continue; - - s << INDENT << "else "; - - QString typeCheck; - QString toCppConv; - QTextStream tcc(&toCppConv); - if (ctor->isConversionOperator()) { - const AbstractMetaClass* metaClass = ctor->ownerClass(); - typeCheck = cpythonCheckFunction(metaClass->typeEntry()); - writeToCppConversion(tcc, metaClass, "pyobj"); - } else { - const AbstractMetaType* argType = ctor->arguments().first()->type(); - typeCheck = cpythonCheckFunction(argType); - writeToCppConversion(tcc, argType, 0, "pyobj"); - } - - s << "if (" << typeCheck << "(pyobj))" << endl; - { - Indentation indent(INDENT); - s << INDENT << "return " << type->name() << '(' << toCppConv << ");" << endl; - } - } - - { - s << INDENT << "else" << endl; - { - Indentation indent(INDENT); - s << INDENT << "return Shiboken::ValueTypeConverter< " << typeName << " >::toCpp(pyobj);" << endl; - } - } + s << INDENT << "PythonToCppFunc toCpp = Shiboken::Conversions::isPythonToCppValueConvertible((SbkObjectType*)"; + s << cpythonTypeNameExt(type) << ", pyIn);" << endl; + s << INDENT << typeName << " cppOut = "; + writeMinimalConstructorExpression(s, type); + s << ';' << endl; + s << INDENT << "toCpp(pyIn, &cppOut);" << endl; + s << INDENT << "return cppOut;" << endl; s << '}' << endl << endl; } @@ -666,4 +606,3 @@ void HeaderGenerator::writeInheritedOverloads(QTextStream& s) s << "; }" << endl; } } - diff --git a/generator/shibokengenerator.cpp b/generator/shibokengenerator.cpp index b6d86394..c344f3c8 100644 --- a/generator/shibokengenerator.cpp +++ b/generator/shibokengenerator.cpp @@ -81,15 +81,17 @@ ShibokenGenerator::ShibokenGenerator() : Generator() m_typeSystemConvName[TypeSystemIsConvertibleFunction] = "isConvertible"; m_typeSystemConvName[TypeSystemToCppFunction] = "toCpp"; m_typeSystemConvName[TypeSystemToPythonFunction] = "toPython"; - m_typeSystemConvRegEx[TypeSystemCheckFunction] = QRegExp("%CHECKTYPE\\[([^\\[]*)\\]"); - m_typeSystemConvRegEx[TypeSystemIsConvertibleFunction] = QRegExp("%ISCONVERTIBLE\\[([^\\[]*)\\]"); - m_typeSystemConvRegEx[TypeSystemToCppFunction] = QRegExp("%CONVERTTOCPP\\[([^\\[]*)\\]"); - m_typeSystemConvRegEx[TypeSystemToPythonFunction] = QRegExp("%CONVERTTOPYTHON\\[([^\\[]*)\\]"); + m_typeSystemConvRegEx[TypeSystemCheckFunction] = QRegExp("%CHECKTYPE\\[([^\\[]*)\\]\\("); + m_typeSystemConvRegEx[TypeSystemIsConvertibleFunction] = QRegExp("%ISCONVERTIBLE\\[([^\\[]*)\\]\\("); + m_typeSystemConvRegEx[TypeSystemToPythonFunction] = QRegExp("%CONVERTTOPYTHON\\[([^\\[]*)\\]\\("); + m_typeSystemConvRegEx[TypeSystemToCppFunction] = QRegExp("((?:[a-zA-Z_%][\\w%]*\\s*[\\*&]?\\s+)*)((?:\\*\\s*)?[a-zA-Z_%][\\w%]*"\ + "(?:\\[[^\\[]+\\])*)(?:\\s+)=(?:\\s+)%CONVERTTOCPP\\[([^\\[]*)\\]\\("); } ShibokenGenerator::~ShibokenGenerator() { - qDeleteAll(m_metaTypeFromStringCache.values()); + // TODO-CONVERTER: it must be caching types that were not created here. + //qDeleteAll(m_metaTypeFromStringCache.values()); } void ShibokenGenerator::clearTpFuncs() @@ -515,9 +517,12 @@ QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType* metaType, QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntry* type, QString argName) { - if (ShibokenGenerator::isWrapperType(type)) - return baseConversionString( "::" + type->qualifiedCppName() + '*') + "toCpp(" + argName + ')'; - return QString(); + if (!ShibokenGenerator::isWrapperType(type)) + return QString(); + return QString("((::%1*)Shiboken::Conversions::cppPointer(%2, (SbkObject*)%3))") + .arg(type->qualifiedCppName()) + .arg(cpythonTypeNameExt(type)) + .arg(argName); } QString ShibokenGenerator::getFunctionReturnType(const AbstractMetaFunction* func, Options options) const @@ -585,28 +590,48 @@ void ShibokenGenerator::writeBaseConversion(QTextStream& s, const AbstractMetaTy void ShibokenGenerator::writeToPythonConversion(QTextStream& s, const AbstractMetaType* type, const AbstractMetaClass* context, const QString& argumentName) { + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(type)) { + s << cpythonToPythonConversionFunction(type) << argumentName << ')'; + return; + } + // TODO-CONVERTER ----------------------------------------------------------------------- s << cpythonToPythonConversionFunction(type, context) << '(' << argumentName << ')'; } void ShibokenGenerator::writeToCppConversion(QTextStream& s, const AbstractMetaClass* metaClass, - const QString& argumentName) + const QString& inArgName, const QString& outArgName) { - s << cpythonToCppConversionFunction(metaClass) << '(' << argumentName << ')'; + // TODO-CONVERTER ----------------------------------------------------------------------- + s << cpythonToCppConversionFunction(metaClass) << inArgName << ", &" << outArgName << ')'; + // TODO-CONVERTER ----------------------------------------------------------------------- + //s << cpythonToCppConversionFunction(metaClass) << '(' << inArgName << ')'; } -void ShibokenGenerator::writeToCppConversion(QTextStream& s, const AbstractMetaType* type, - const AbstractMetaClass* context, const QString& argumentName) +void ShibokenGenerator::writeToCppConversion(QTextStream& s, const AbstractMetaType* type, const AbstractMetaClass* context, + const QString& inArgName, const QString& outArgName) { - s << cpythonToCppConversionFunction(type, context) << '(' << argumentName << ')'; + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(type)) { + s << cpythonToCppConversionFunction(type, context) << inArgName << ", &" << outArgName << ')'; + return; + } + // TODO-CONVERTER ----------------------------------------------------------------------- + s << cpythonToCppConversionFunction(type, context) << '(' << inArgName << ')'; } bool ShibokenGenerator::shouldRejectNullPointerArgument(const AbstractMetaFunction* func, int argIndex) { if (argIndex < 0 || argIndex >= func->arguments().count()) return false; + // TODO-CONVERTER ----------------------------------------------------------------------- + const AbstractMetaArgument* arg = func->arguments().at(argIndex); + if (isValueTypeWithCopyConstructorOnly(arg->type())) + return true; + // TODO-CONVERTER ----------------------------------------------------------------------- // Argument type is not a pointer, a None rejection should not be // necessary because the type checking would handle that already. - if (!isPointer(func->arguments().at(argIndex)->type())) + if (!isPointer(arg->type())) return false; if (func->argumentRemoved(argIndex + 1)) return false; @@ -752,6 +777,36 @@ QString ShibokenGenerator::cpythonOperatorFunctionName(const AbstractMetaFunctio + '_' + pythonOperatorFunctionName(func->originalName()); } +QString ShibokenGenerator::fixedCppTypeName(const CustomConversion::TargetToNativeConversion* toNative) +{ + if (toNative->sourceType()) + return fixedCppTypeName(toNative->sourceType()); + return toNative->sourceTypeName(); +} +QString ShibokenGenerator::fixedCppTypeName(const AbstractMetaType* type) +{ + return fixedCppTypeName(type->typeEntry(), type->cppSignature()); +} + +static QString _fixedCppTypeName(QString typeName) +{ + return typeName.replace(" ", "") + .replace(".", "_") + .replace("<", "_") + .replace(">", "_") + .replace("::", "_") + .replace("*", "PTR") + .replace("&", "REF"); +} +QString ShibokenGenerator::fixedCppTypeName(const TypeEntry* type, QString typeName) +{ + if (typeName.isEmpty()) + typeName = type->qualifiedCppName(); + if (!(type->codeGeneration() & TypeEntry::GenerateTargetLang)) + typeName.prepend(QString("%1_").arg(type->targetLangPackage())); + return _fixedCppTypeName(typeName); +} + QString ShibokenGenerator::pythonPrimitiveTypeName(const QString& cppTypeName) { return ShibokenGenerator::m_pythonPrimitiveTypeName.value(cppTypeName, QString()); @@ -867,6 +922,30 @@ bool ShibokenGenerator::isPointerToWrapperType(const AbstractMetaType* type) return (isObjectType(type) && type->indirections() == 1) || type->isValuePointer(); } +bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const AbstractMetaClass* metaClass) +{ + if (!metaClass || !metaClass->typeEntry()->isValue()) + return false; + AbstractMetaFunctionList ctors = metaClass->queryFunctions(AbstractMetaClass::Constructors); + if (ctors.count() != 1) + return false; + return ctors.first()->isCopyConstructor(); +} + +bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const TypeEntry* type) const +{ + if (!type || !type->isValue()) + return false; + return isValueTypeWithCopyConstructorOnly(classes().findClass(type)); +} + +bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const AbstractMetaType* type) const +{ + if (!type || !type->typeEntry()->isValue()) + return false; + return isValueTypeWithCopyConstructorOnly(type->typeEntry()); +} + bool ShibokenGenerator::shouldDereferenceArgumentPointer(const AbstractMetaArgument* arg) { return shouldDereferenceAbstractMetaTypePointer(arg->type()); @@ -874,8 +953,7 @@ bool ShibokenGenerator::shouldDereferenceArgumentPointer(const AbstractMetaArgum bool ShibokenGenerator::shouldDereferenceAbstractMetaTypePointer(const AbstractMetaType* metaType) { - return isWrapperType(metaType) && !isPointer(metaType) - && (metaType->isValue() || metaType->isReference()); + return metaType->isReference() && isWrapperType(metaType) && !isPointer(metaType); } bool ShibokenGenerator::visibilityModifiedToPrivate(const AbstractMetaFunction* func) @@ -899,6 +977,12 @@ QString ShibokenGenerator::cpythonCheckFunction(const AbstractMetaType* metaType return customCheck; } + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(metaType)) { + return QString("SbkObject_TypeCheck(%1, ").arg(cpythonTypeNameExt(metaType->typeEntry())); + } + // TODO-CONVERTER ----------------------------------------------------------------------- + QString baseName = cpythonBaseName(metaType); if (isNumber(baseName)) return genericNumberType ? QString("SbkNumber_Check") : QString("%1_Check").arg(baseName); @@ -924,6 +1008,12 @@ QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry* type, bool gene return customCheck; } + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(type)) { + return QString("SbkObject_TypeCheck(%1, ").arg(cpythonTypeNameExt(type)); + } + // TODO-CONVERTER ----------------------------------------------------------------------- + QString baseName = cpythonBaseName(type); if (isNumber(baseName)) return genericNumberType ? "SbkNumber_Check" : baseName+"_Check"; @@ -967,6 +1057,16 @@ QString ShibokenGenerator::guessCPythonIsConvertible(const QString& type) QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntry* type, bool genericNumberType, bool checkExact) { + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(type)) { + QString isConv = (type->isValue() && !isValueTypeWithCopyConstructorOnly(type)) + ? "isPythonToCppValueConvertible" + : "isPythonToCppPointerConvertible"; + return QString("Shiboken::Conversions::%1((SbkObjectType*)%2, ") + .arg(isConv).arg(cpythonTypeNameExt(type)); + } + // TODO-CONVERTER ----------------------------------------------------------------------- + if (checkExact) return cpythonCheckFunction(type, genericNumberType); @@ -990,6 +1090,20 @@ QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaType* return customCheck; } + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(metaType)) { + QString isConv; + if (isPointer(metaType) || isValueTypeWithCopyConstructorOnly(metaType)) + isConv = "isPythonToCppPointerConvertible"; + else if (metaType->isReference()) + isConv = "isPythonToCppReferenceConvertible"; + else + isConv = "isPythonToCppValueConvertible"; + return QString("Shiboken::Conversions::%1((SbkObjectType*)%2, ") + .arg(isConv).arg(cpythonTypeNameExt(metaType)); + } + // TODO-CONVERTER ----------------------------------------------------------------------- + QString baseName = cpythonBaseName(metaType); if (isNumber(baseName)) return genericNumberType ? QString("SbkNumber_Check") : QString("%1_Check").arg(baseName); @@ -1002,13 +1116,24 @@ QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaType* QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaClass* metaClass) { - QString base; - QTextStream b(&base); - writeBaseConversion(b, metaClass->typeEntry()); - return QString("%1toCpp").arg(base); + // TODO-CONVERTER ----------------------------------------------------------------------- + return QString("Shiboken::Conversions::pythonToCppPointer((SbkObjectType*)%1, ") + .arg(cpythonTypeNameExt(metaClass->typeEntry())); + // TODO-CONVERTER ----------------------------------------------------------------------- + //QString base; + //QTextStream b(&base); + //writeBaseConversion(b, metaClass->typeEntry()); + //return QString("%1toCpp").arg(base); } QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaType* type, const AbstractMetaClass* context) { + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(type)) { + return QString("Shiboken::Conversions::pythonToCpp%1((SbkObjectType*)%2, ") + .arg(isPointer(type) ? "Pointer" : "Copy") + .arg(cpythonTypeNameExt(type)); + } + // TODO-CONVERTER ----------------------------------------------------------------------- QString base; QTextStream b(&base); writeBaseConversion(b, type, context); @@ -1017,6 +1142,19 @@ QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaType QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaType* type, const AbstractMetaClass* context) { + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(type)) { + QString conversion; + if (type->isReference() && !isPointer(type)) + conversion = "reference"; + else if (type->isValue()) + conversion = "copy"; + else + conversion = "pointer"; + return QString("Shiboken::Conversions::%1ToPython((SbkObjectType*)%2, %3") + .arg(conversion).arg(cpythonTypeNameExt(type)).arg(conversion == "pointer" ? "" : "&"); + } + // TODO-CONVERTER ----------------------------------------------------------------------- // exclude const on Objects Options flags = getConverterOptions(type); QString base; @@ -1032,6 +1170,17 @@ QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaC QString ShibokenGenerator::cpythonToPythonConversionFunction(const TypeEntry* type) { + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(type)) { + QString conversion; + if (type->isValue()) + conversion = "copy"; + else + conversion = "pointer"; + return QString("Shiboken::Conversions::%1ToPython((SbkObjectType*)%2, %3") + .arg(conversion).arg(cpythonTypeNameExt(type)).arg(conversion == "pointer" ? "" : "&"); + } + // TODO-CONVERTER ----------------------------------------------------------------------- QString base; QTextStream b(&base); writeBaseConversion(b, type); @@ -1312,12 +1461,25 @@ ShibokenGenerator::ArgumentVarReplacementList ShibokenGenerator::getArgumentRepl argValue = QString(CPP_ARG_REMOVED"%1").arg(i); if (!argRemoved && argValue.isEmpty()) { int argPos = i - removed; - if (arg->type()->typeEntry()->isCustom()) { + const AbstractMetaType* type = arg->type(); + QString typeReplaced = func->typeReplaced(arg->argumentIndex() + 1); + if (!typeReplaced.isEmpty()) { + AbstractMetaType* builtType = buildAbstractMetaTypeFromString(typeReplaced); + if (builtType) + type = builtType; + } + if (type->typeEntry()->isCustom()) { argValue = usePyArgs ? QString(PYTHON_ARGS"[%1]").arg(argPos) : PYTHON_ARG; } else { argValue = hasConversionRule ? QString("%1"CONV_RULE_OUT_VAR_SUFFIX).arg(arg->name()) : QString(CPP_ARG"%1").arg(argPos); + // TODO-CONVERTER ----------------------------------------------------------------------- + if (isWrapperType(type)) { + if (type->isReference() && !isPointer(type)) + argValue.prepend('*'); + } + // TODO-CONVERTER ----------------------------------------------------------------------- } } } else { @@ -1489,10 +1651,23 @@ void ShibokenGenerator::writeCodeSnips(QTextStream& s, foreach (ArgumentVarReplacementPair pair, argReplacements) { const AbstractMetaArgument* arg = pair.first; int idx = arg->argumentIndex() + 1; - QString replacement = pair.second; - if (isWrapperType(arg->type()) && isPointer(arg->type())) - code.replace(QString("%%1.").arg(idx), QString("%1->").arg(replacement)); - code.replace(QString("%%1").arg(idx), replacement); + // TODO-CONVERTER ----------------------------------------------------------------------- + AbstractMetaType* type = arg->type(); + QString typeReplaced = func->typeReplaced(arg->argumentIndex() + 1); + if (!typeReplaced.isEmpty()) { + AbstractMetaType* builtType = buildAbstractMetaTypeFromString(typeReplaced); + if (builtType) + type = builtType; + } + if (isWrapperType(type)) { + QString replacement = pair.second; + if (type->isReference() && !isPointer(type)) + replacement.remove(0, 1); + if (type->isReference() || isPointer(type)) + code.replace(QString("%%1.").arg(idx), QString("%1->").arg(replacement)); + } + // TODO-CONVERTER ----------------------------------------------------------------------- + code.replace(QString("%%1").arg(idx), pair.second); } if (language == TypeSystem::NativeCode) { @@ -1541,6 +1716,52 @@ void ShibokenGenerator::writeCodeSnips(QTextStream& s, s << INDENT << "// End of code injection" << endl; } +// Returns true if the string is an expression, +// and false if it is a variable. +static bool isVariable(const QString& code) +{ + static QRegExp expr("\\s*\\*?\\s*[A-Za-z_]+[A-Za-z_0-9]*\\s*(?:\\[[^\\[]+\\])*"); + return expr.exactMatch(code.trimmed()); +} + +// A miniature normalizer that puts a type string into a format +// suitable for comparison with AbstractMetaType::cppSignature() +// result. +static QString miniNormalizer(const QString& varType) +{ + QString normalized = varType.trimmed(); + QString suffix; + while (normalized.endsWith('*') || normalized.endsWith('&')) { + suffix.prepend(normalized.at(normalized.count() - 1)); + normalized.chop(1); + } + return QString("%1 %2").arg(normalized).arg(suffix).trimmed(); +} +// The position must indicate the first character after the opening '('. +// ATTENTION: do not modify this function to trim any resulting string! +// This must be done elsewhere. +static QString getConverterTypeSystemVariableArgument(const QString& code, int pos) +{ + QString arg; + int parenthesisDepth = 0; + int count = 0; + while (pos + count < code.count()) { + char c = code.at(pos+count).toAscii(); + if (c == '(') { + ++parenthesisDepth; + } else if (c == ')') { + if (parenthesisDepth == 0) { + arg = code.mid(pos, count).trimmed(); + break; + } + --parenthesisDepth; + } + ++count; + } + if (parenthesisDepth != 0) + qFatal("Unbalanced parenthesis on type system converter variable call."); + return arg; +} typedef QPair<QString, QString> StringPair; void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString& code) { @@ -1551,31 +1772,72 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa while ((pos = regex.indexIn(code, pos)) != -1) { pos += regex.matchedLength(); QStringList list = regex.capturedTexts(); - QString conversionVar = list.first(); + QString conversionString = list.first(); QString conversionTypeName = list.last(); const AbstractMetaType* conversionType = buildAbstractMetaTypeFromString(conversionTypeName); QString conversion; + QTextStream c(&conversion); if (conversionType) { switch (converterVariable) { + case TypeSystemToCppFunction: { + if (!isWrapperType(conversionType)) { + c << list.at(1) << list.at(2) << " = "; + c << cpythonToCppConversionFunction(conversionType); + c << '('; + break; + } + QString varType = miniNormalizer(list.at(1)); + QString varName = list.at(2).trimmed(); + if (!varType.isEmpty()) { + if (varType != conversionType->cppSignature()) { + qFatal(qPrintable(QString("Types of receiver variable ('%1') and %CONVERTTOCPP type system variable ('%2') differ.") + .arg(varType).arg(conversionType->cppSignature())), NULL); + } + c << getFullTypeName(conversionType) << ' ' << varName << " = "; + writeMinimalConstructorExpression(c, conversionType); + c << ';' << endl; + Indentation indent(INDENT); + c << INDENT; + } + c << cpythonToCppConversionFunction(conversionType); + QString prefix; + if (varName.startsWith('*')) { + varName.chop(1); + varName = varName.trimmed(); + } else { + prefix = '&'; + } + QString arg = getConverterTypeSystemVariableArgument(code, pos); + conversionString += arg; + c << arg << ", " << prefix << '(' << varName << ')'; + break; + } case TypeSystemCheckFunction: conversion = cpythonCheckFunction(conversionType); - break; case TypeSystemIsConvertibleFunction: - conversion = cpythonIsConvertibleFunction(conversionType); - break; - case TypeSystemToCppFunction: - conversion = cpythonToCppConversionFunction(conversionType); - break; + if (conversion.isEmpty()) + conversion = cpythonIsConvertibleFunction(conversionType); case TypeSystemToPythonFunction: - conversion = cpythonToPythonConversionFunction(conversionType); - break; - default: - Q_ASSERT(false); + if (conversion.isEmpty()) + conversion = cpythonToPythonConversionFunction(conversionType); + default: { + if (!isWrapperType(conversionType)) { + c << '('; + break; + } + 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; + } } } else { - conversion = QString("Shiboken::Converter< %1 >::%2").arg(conversionTypeName).arg(conversionName); + conversion = QString("Shiboken::Converter< %1 >::%2(").arg(conversionTypeName).arg(conversionName); } - replacements.append(qMakePair(conversionVar, conversion)); + replacements.append(qMakePair(conversionString, conversion)); } foreach (StringPair rep, replacements) code.replace(rep.first, rep.second); @@ -1817,6 +2079,24 @@ AbstractMetaType* ShibokenGenerator::buildAbstractMetaTypeFromString(QString typ return metaType; } +AbstractMetaType* ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(const TypeEntry* typeEntry) +{ + if (m_metaTypeFromStringCache.contains(typeEntry->qualifiedCppName())) + return m_metaTypeFromStringCache.value(typeEntry->qualifiedCppName()); + AbstractMetaType* metaType = new AbstractMetaType; + metaType->setTypeEntry(typeEntry); + metaType->setIndirections(0); + metaType->setReference(false); + metaType->setConstant(false); + metaType->decideUsagePattern(); + m_metaTypeFromStringCache.insert(typeEntry->qualifiedCppName(), metaType); + return metaType; +} +AbstractMetaType* ShibokenGenerator::buildAbstractMetaTypeFromAbstractMetaClass(const AbstractMetaClass* metaClass) +{ + return ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(metaClass->typeEntry()); +} + /* static void dumpFunction(AbstractMetaFunctionList lst) { @@ -1946,17 +2226,6 @@ QString ShibokenGenerator::cppApiVariableName(const QString& moduleName) const return result; } -static QString _fixedCppTypeName(QString typeName) -{ - return typeName.replace(" ", "") - .replace(".", "_") - .replace("<", "_") - .replace(">", "_") - .replace("::", "_") - .replace("*", "PTR") - .replace("&", "REF"); -} - static QString processInstantiationsVariableName(const AbstractMetaType* type) { QString res = QString("_%1").arg(_fixedCppTypeName(type->typeEntry()->qualifiedCppName()).toUpper()); @@ -2001,6 +2270,19 @@ QString ShibokenGenerator::getFullTypeName(const AbstractMetaClass* metaClass) { return getFullTypeName(metaClass->typeEntry()); } +QString ShibokenGenerator::getFullTypeNameWithoutModifiers(const AbstractMetaType* type) +{ + if (!type->hasInstantiations()) + return getFullTypeName(type->typeEntry()); + QString typeName = type->cppSignature(); + if (type->isConstant()) + typeName.remove(0, sizeof("const ") / sizeof(char) - 1); + if (type->isReference()) + typeName.chop(1); + while (typeName.endsWith('*') || typeName.endsWith(' ')) + typeName.chop(1); + return QString("::%1").arg(typeName); +} bool ShibokenGenerator::verboseErrorMessagesDisabled() const { @@ -2052,3 +2334,28 @@ QString ShibokenGenerator::getDefaultValue(const AbstractMetaFunction* func, co } return QString(); } + +// TODO-CONVERTER ----------------------------------------------------------------------- +void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const AbstractMetaType* type, const QString& defaultCtor) +{ + QString ctor = defaultCtor.isEmpty() ? minimalConstructor(type) : defaultCtor; + if (ctor.isEmpty()) { + QString errorMsg = QString(MIN_CTOR_ERROR_MSG).arg(type->cppSignature()); + ReportHandler::warning(errorMsg); + s << endl << INDENT << "#error " << errorMsg << endl; + return; + } + s << ctor; +} +void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const TypeEntry* type, const QString& defaultCtor) +{ + QString ctor = defaultCtor.isEmpty() ? minimalConstructor(type) : defaultCtor; + if (ctor.isEmpty()) { + QString errorMsg = QString(MIN_CTOR_ERROR_MSG).arg(type->qualifiedCppName()); + ReportHandler::warning(errorMsg); + s << endl << INDENT << "#error " << errorMsg << endl; + return; + } + s << ctor; +} +// TODO-CONVERTER ----------------------------------------------------------------------- diff --git a/generator/shibokengenerator.h b/generator/shibokengenerator.h index ca1a273a..05e5bd92 100644 --- a/generator/shibokengenerator.h +++ b/generator/shibokengenerator.h @@ -40,6 +40,7 @@ #define END_ALLOW_THREADS "PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS" #define MIN_CTOR_ERROR_MSG "Could not find a minimal constructor for type '%1'. "\ "This will result in a compilation error." +#define PYTHON_TO_CPP_VAR "pythonToCpp" #include <generator.h> #include <QtCore/QTextStream> @@ -247,11 +248,11 @@ public: void writeBaseConversion(QTextStream& s, const TypeEntry* type); void writeToPythonConversion(QTextStream& s, const AbstractMetaType* type, const AbstractMetaClass* context, const QString& argumentName); - void writeToCppConversion(QTextStream& s, const AbstractMetaType* type, const AbstractMetaClass* context, const QString& argumentName); - void writeToCppConversion(QTextStream& s, const AbstractMetaClass* metaClass, const QString& argumentName); + void writeToCppConversion(QTextStream& s, const AbstractMetaType* type, const AbstractMetaClass* context, const QString& inArgName, const QString& outArgName); + void writeToCppConversion(QTextStream& s, const AbstractMetaClass* metaClass, const QString& inArgName, const QString& outArgName); /// Returns true if the argument is a pointer that rejects NULL values. - static bool shouldRejectNullPointerArgument(const AbstractMetaFunction* func, int argIndex); + bool shouldRejectNullPointerArgument(const AbstractMetaFunction* func, int argIndex); /// Verifies if the class should have a C++ wrapper generated for it, instead of only a Python wrapper. bool shouldGenerateCppWrapper(const AbstractMetaClass* metaClass) const; @@ -278,6 +279,10 @@ public: static QString cpythonOperatorFunctionName(const AbstractMetaFunction* func); + static QString fixedCppTypeName(const CustomConversion::TargetToNativeConversion* toNative); + static QString fixedCppTypeName(const AbstractMetaType* type); + static QString fixedCppTypeName(const TypeEntry* type, QString typeName = QString()); + static bool isNumber(QString cpythonApiName); static bool isNumber(const TypeEntry* type); static bool isNumber(const AbstractMetaType* type); @@ -300,6 +305,11 @@ public: */ static bool isPointerToWrapperType(const AbstractMetaType* type); + static bool isValueTypeWithCopyConstructorOnly(const AbstractMetaClass* metaClass); + bool isValueTypeWithCopyConstructorOnly(const TypeEntry* type) const; + bool isValueTypeWithCopyConstructorOnly(const AbstractMetaType* type) const; + + /// Checks if an argument type should be dereferenced by the Python method wrapper before calling the C++ method. static bool shouldDereferenceArgumentPointer(const AbstractMetaArgument* arg); /// Checks if a meta type should be dereferenced by the Python method wrapper passing it to C++. @@ -409,6 +419,13 @@ public: QString getFullTypeName(const AbstractMetaType* type); QString getFullTypeName(const AbstractMetaClass* metaClass); + /** + * Returns the full qualified C++ name for an AbstractMetaType, but removing modifiers + * as 'const', '&', and '*' (except if the class is not derived from a template). + * This is useful for instantiated templates. + */ + QString getFullTypeNameWithoutModifiers(const AbstractMetaType* type); + /// Returns true if the user don't want verbose error messages on the generated bindings. bool verboseErrorMessagesDisabled() const; @@ -420,6 +437,14 @@ public: */ AbstractMetaType* buildAbstractMetaTypeFromString(QString typeSignature); + /// Creates an AbstractMetaType object from a TypeEntry. + AbstractMetaType* buildAbstractMetaTypeFromTypeEntry(const TypeEntry* typeEntry); + /// Creates an AbstractMetaType object from an AbstractMetaClass. + AbstractMetaType* buildAbstractMetaTypeFromAbstractMetaClass(const AbstractMetaClass* metaClass); + + void writeMinimalConstructorExpression(QTextStream& s, const AbstractMetaType* type, const QString& defaultCtor = QString()); + void writeMinimalConstructorExpression(QTextStream& s, const TypeEntry* type, const QString& defaultCtor = QString()); + /** * Helper function to return the flags to be used by a meta type when * it needs to write some converter code. diff --git a/libshiboken/CMakeLists.txt b/libshiboken/CMakeLists.txt index b57a814c..342151af 100644 --- a/libshiboken/CMakeLists.txt +++ b/libshiboken/CMakeLists.txt @@ -27,6 +27,7 @@ set(libshiboken_SRC basewrapper.cpp gilstate.cpp helper.cpp +sbkconverter.cpp sbkenum.cpp sbkmodule.cpp sbkstring.cpp @@ -54,6 +55,7 @@ install(FILES conversions.h gilstate.h helper.h + sbkconverter.h sbkenum.h sbkmodule.h python25compat.h diff --git a/libshiboken/basewrapper.cpp b/libshiboken/basewrapper.cpp index 2baa5b85..181b7025 100644 --- a/libshiboken/basewrapper.cpp +++ b/libshiboken/basewrapper.cpp @@ -22,6 +22,7 @@ #include "basewrapper.h" #include "basewrapper_p.h" +#include "sbkconverter.h" #include "sbkenum.h" #include "autodecref.h" #include "typeresolver.h" @@ -250,6 +251,8 @@ void SbkObjectTypeDealloc(PyObject* pyObj) } free(sbkType->d->original_name); sbkType->d->original_name = 0; + if (!Shiboken::ObjectType::isUserType(reinterpret_cast<PyTypeObject*>(sbkType))) + Shiboken::Conversions::deleteConverter(sbkType->d->converter); delete sbkType->d; sbkType->d = 0; } @@ -297,20 +300,18 @@ PyObject* SbkObjectTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* k d->mi_offsets = parentType->mi_offsets; d->mi_init = parentType->mi_init; d->mi_specialcast = parentType->mi_specialcast; - d->ext_isconvertible = parentType->ext_isconvertible; - d->ext_tocpp = parentType->ext_tocpp; d->type_discovery = parentType->type_discovery; d->cpp_dtor = parentType->cpp_dtor; d->is_multicpp = 0; + d->converter = parentType->converter; } else { d->mi_offsets = 0; d->mi_init = 0; d->mi_specialcast = 0; - d->ext_isconvertible = 0; - d->ext_tocpp = 0; d->type_discovery = 0; d->cpp_dtor = 0; d->is_multicpp = 1; + d->converter = 0; } if (bases.size() == 1) d->original_name = strdup(bases.front()->d->original_name); @@ -596,30 +597,13 @@ bool canCallConstructor(PyTypeObject* myType, PyTypeObject* ctorType) return true; } -bool hasExternalCppConversions(SbkObjectType* self) -{ - return self->d->ext_tocpp; -} - -void* callExternalCppConversion(SbkObjectType* self, PyObject* obj) -{ - return self->d->ext_tocpp(obj); -} - -void setExternalCppConversionFunction(SbkObjectType* self, ExtendedToCppFunc func) -{ - self->d->ext_tocpp = func; -} -void setExternalIsConvertibleFunction(SbkObjectType* self, ExtendedIsConvertibleFunc func) -{ - self->d->ext_isconvertible = func; -} +bool hasExternalCppConversions(SbkObjectType*) { return false; } // DEPRECATED. +bool isExternalConvertible(SbkObjectType* self, PyObject* obj) { return false; } // DEPRECATED. +void setExternalCppConversionFunction(SbkObjectType*, ExtendedToCppFunc) {} // DEPRECATED. +void setExternalIsConvertibleFunction(SbkObjectType*, ExtendedIsConvertibleFunc) {} // DEPRECATED. +void* callExternalCppConversion(SbkObjectType*, PyObject*) { return 0; } // DEPRECATED. -bool isExternalConvertible(SbkObjectType* self, PyObject* obj) -{ - return self->d->ext_isconvertible && self->d->ext_isconvertible(obj); -} bool hasCast(SbkObjectType* type) { @@ -741,6 +725,12 @@ void setTypeUserData(SbkObjectType* self, void* userData, DeleteUserDataFunc d_f self->d->d_func = d_func; } + +SbkConverter* getTypeConverter(SbkObjectType* type) +{ + return type->d->converter; +} + } // namespace ObjectType diff --git a/libshiboken/basewrapper.h b/libshiboken/basewrapper.h index 1d4b9643..c2539027 100644 --- a/libshiboken/basewrapper.h +++ b/libshiboken/basewrapper.h @@ -33,6 +33,7 @@ extern "C" { +struct SbkConverter; struct SbkObjectPrivate; /// Base Python object for all the wrapped C++ classes. @@ -65,8 +66,8 @@ typedef void* (*SpecialCastFunction)(void*, SbkObjectType*); typedef SbkObjectType* (*TypeDiscoveryFunc)(void*, SbkObjectType*); typedef void* (*TypeDiscoveryFuncV2)(void*, SbkObjectType*); -typedef void* (*ExtendedToCppFunc)(PyObject*); -typedef bool (*ExtendedIsConvertibleFunc)(PyObject*); +typedef void* (*ExtendedToCppFunc)(PyObject*); // DEPRECATED. +typedef bool (*ExtendedIsConvertibleFunc)(PyObject*); // DEPRECATED. // Used in userdata dealloc function typedef void (*DeleteUserDataFunc)(void*); @@ -131,11 +132,13 @@ LIBSHIBOKEN_API bool isUserType(PyTypeObject* pyObj); */ LIBSHIBOKEN_API bool canCallConstructor(PyTypeObject* myType, PyTypeObject* ctorType); -LIBSHIBOKEN_API void setExternalCppConversionFunction(SbkObjectType* self, ExtendedToCppFunc func); -LIBSHIBOKEN_API void setExternalIsConvertibleFunction(SbkObjectType* self, ExtendedIsConvertibleFunc func); -LIBSHIBOKEN_API bool hasExternalCppConversions(SbkObjectType* self); -LIBSHIBOKEN_API bool isExternalConvertible(SbkObjectType* self, PyObject* obj); -LIBSHIBOKEN_API void* callExternalCppConversion(SbkObjectType* self, PyObject* obj); + +LIBSHIBOKEN_API bool hasExternalCppConversions(SbkObjectType*); // DEPRECATED. +LIBSHIBOKEN_API bool isExternalConvertible(SbkObjectType*, PyObject*); // DEPRECATED. +LIBSHIBOKEN_API void setExternalCppConversionFunction(SbkObjectType*, ExtendedToCppFunc); // DEPRECATED. +LIBSHIBOKEN_API void setExternalIsConvertibleFunction(SbkObjectType*, ExtendedIsConvertibleFunc); // DEPRECATED. +LIBSHIBOKEN_API void* callExternalCppConversion(SbkObjectType*, PyObject*); // DEPRECATED. + /** * Tells if the \p type represents an object of a class with multiple inheritance in C++. @@ -206,6 +209,10 @@ LIBSHIBOKEN_API void setSubTypeInitHook(SbkObjectType* self, SubTypeInitH */ LIBSHIBOKEN_API void* getTypeUserData(SbkObjectType* self); LIBSHIBOKEN_API void setTypeUserData(SbkObjectType* self, void* userData, DeleteUserDataFunc d_func); + +/// Returns the converter assigned to the wrapper \p type. +LIBSHIBOKEN_API SbkConverter* getTypeConverter(SbkObjectType* type); + } namespace Object { diff --git a/libshiboken/basewrapper_p.h b/libshiboken/basewrapper_p.h index 9f96ba75..c943a2a0 100644 --- a/libshiboken/basewrapper_p.h +++ b/libshiboken/basewrapper_p.h @@ -31,6 +31,7 @@ struct SbkObject; struct SbkObjectType; +struct SbkConverter; namespace Shiboken { @@ -106,10 +107,6 @@ struct SbkObjectTypePrivate /// Special cast function, null if this class doesn't have multiple inheritance. SpecialCastFunction mi_specialcast; TypeDiscoveryFuncV2 type_discovery; - /// Extended "isConvertible" function to be used when a conversion operator is defined in another module. - ExtendedIsConvertibleFunc ext_isconvertible; - /// Extended "toCpp" function to be used when a conversion operator is defined in another module. - ExtendedToCppFunc ext_tocpp; /// Pointer to a function responsible for deletion of the C++ instance calling the proper destructor. ObjectDestructor cpp_dtor; /// True if this type holds two or more C++ instances, e.g.: a Python class which inherits from two C++ classes. @@ -124,6 +121,7 @@ struct SbkObjectTypePrivate void* user_data; DeleteUserDataFunc d_func; void (*subtype_init)(SbkObjectType*, PyObject*, PyObject*); + SbkConverter* converter; }; diff --git a/libshiboken/sbkconverter.cpp b/libshiboken/sbkconverter.cpp new file mode 100644 index 00000000..b252e16c --- /dev/null +++ b/libshiboken/sbkconverter.cpp @@ -0,0 +1,214 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team <contact@pyside.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "sbkconverter.h" +#include "sbkconverter_p.h" +#include "basewrapper_p.h" + +#include "sbkdbg.h" + +namespace Shiboken { +namespace Conversions { + +static SbkConverter* createConverterObject(PyTypeObject* type, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc) +{ + SbkConverter* converter = new SbkConverter; + converter->pythonType = type; + + converter->pointerToPython = pointerToPythonFunc; + converter->copyToPython = copyToPythonFunc; + + converter->toCppPointerConversion = std::make_pair(toCppPointerCheckFunc, toCppPointerConvFunc); + converter->toCppConversions.clear(); + + return converter; +} + +SbkConverter* createConverter(SbkObjectType* type, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc) +{ + SbkConverter* converter = createConverterObject((PyTypeObject*)type, + toCppPointerConvFunc, toCppPointerCheckFunc, + pointerToPythonFunc, copyToPythonFunc); + type->d->converter = converter; + return converter; +} + +void deleteConverter(SbkConverter* converter) +{ + if (converter) { + converter->toCppConversions.clear(); + delete converter; + } +} + +void addPythonToCppValueConversion(SbkConverter* converter, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc) +{ + converter->toCppConversions.push_back(std::make_pair(isConvertibleToCppFunc, pythonToCppFunc)); +} +void addPythonToCppValueConversion(SbkObjectType* type, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc) +{ + addPythonToCppValueConversion(type->d->converter, pythonToCppFunc, isConvertibleToCppFunc); +} + +PyObject* pointerToPython(SbkObjectType* type, const void* cppIn) +{ + if (!cppIn) + Py_RETURN_NONE; + return type->d->converter->pointerToPython(cppIn); +} + +PyObject* referenceToPython(SbkObjectType* type, const void* cppIn) +{ + assert(cppIn); + + // If it is a Object Type, produce a wrapper for it. + if (!type->d->converter->copyToPython) + return type->d->converter->pointerToPython(cppIn); + + // If it is a Value Type, try to find an existing wrapper, otherwise copy it as value to Python. + PyObject* pyOut = (PyObject*)BindingManager::instance().retrieveWrapper(cppIn); + if (pyOut) { + Py_INCREF(pyOut); + return pyOut; + } + return type->d->converter->copyToPython(cppIn); +} + +static inline PyObject* CopyCppToPython(SbkConverter* converter, const void* cppIn) +{ + assert(cppIn); + return converter->copyToPython(cppIn); +} +PyObject* copyToPython(SbkObjectType* type, const void* cppIn) +{ + return CopyCppToPython(type->d->converter, cppIn); +} +PyObject* copyToPython(SbkConverter* converter, const void* cppIn) +{ + return CopyCppToPython(converter, cppIn); +} + +PythonToCppFunc isPythonToCppPointerConvertible(SbkObjectType* type, PyObject* pyIn) +{ + assert(pyIn); + return type->d->converter->toCppPointerConversion.first(pyIn); +} + +static inline PythonToCppFunc IsPythonToCppConvertible(SbkConverter* converter, PyObject* pyIn) +{ + assert(pyIn); + ToCppConversionList& convs = converter->toCppConversions; + for (ToCppConversionList::iterator conv = convs.begin(); conv != convs.end(); ++conv) { + PythonToCppFunc toCppFunc = 0; + if ((toCppFunc = (*conv).first(pyIn))) + return toCppFunc; + } + return 0; +} +PythonToCppFunc isPythonToCppValueConvertible(SbkObjectType* type, PyObject* pyIn) +{ + return IsPythonToCppConvertible(type->d->converter, pyIn); +} +PythonToCppFunc isPythonToCppConvertible(SbkConverter* converter, PyObject* pyIn) +{ + return IsPythonToCppConvertible(converter, pyIn); +} + +PythonToCppFunc isPythonToCppReferenceConvertible(SbkObjectType* type, PyObject* pyIn) +{ + if (pyIn != Py_None) { + PythonToCppFunc toCpp = isPythonToCppPointerConvertible(type, pyIn); + if (toCpp) + return toCpp; + } + return isPythonToCppValueConvertible(type, pyIn); +} + +void nonePythonToCppNullPtr(PyObject*, void* cppOut) +{ + assert(cppOut); + *((void**)cppOut) = 0; +} + +void* cppPointer(PyTypeObject* desiredType, SbkObject* pyIn) +{ + assert(pyIn); + SbkObjectType* inType = (SbkObjectType*)pyIn->ob_type; + if (ObjectType::hasCast(inType)) + return ObjectType::cast(inType, pyIn, desiredType); + return Object::cppPointer(pyIn, desiredType); +} + +void pythonToCppPointer(SbkObjectType* type, PyObject* pyIn, void* cppOut) +{ + assert(type); + assert(pyIn); + assert(cppOut); + *((void**)cppOut) = (pyIn == Py_None) ? 0 : cppPointer((PyTypeObject*)type, (SbkObject*)pyIn); +} + +void pythonToCppCopy(SbkObjectType* type, PyObject* pyIn, void* cppOut) +{ + assert(type); + assert(pyIn); + assert(cppOut); + PythonToCppFunc toCpp = IsPythonToCppConvertible(type->d->converter, pyIn); + if (toCpp) + toCpp(pyIn, cppOut); +} + +bool isImplicitConversion(SbkObjectType* type, PythonToCppFunc toCppFunc) +{ + // This is the Object Type or Value Type conversion that only + // retrieves the C++ pointer held in the Python wrapper. + if (toCppFunc == type->d->converter->toCppPointerConversion.second) + return false; + + // Object Types doesn't have any kind of value conversion, + // only C++ pointer retrieval. + if (type->d->converter->toCppConversions.empty()) + return false; + + // The first conversion of the non-pointer conversion list is + // a Value Type's copy to C++ function, which is not an implicit + // conversion. + // Otherwise it must be one of the implicit conversions. + // Note that we don't check if the Python to C++ conversion is in + // the list of the type's conversions, for it is expected that the + // caller knows what he's doing. + ToCppConversionList::iterator conv = type->d->converter->toCppConversions.begin(); + return toCppFunc != (*conv).second; +} + +} } // namespace Shiboken::Conversions diff --git a/libshiboken/sbkconverter.h b/libshiboken/sbkconverter.h new file mode 100644 index 00000000..d7be2577 --- /dev/null +++ b/libshiboken/sbkconverter.h @@ -0,0 +1,205 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team <contact@pyside.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SBK_CONVERTER_H +#define SBK_CONVERTER_H + +#include <limits> +#include <Python.h> +#include "shibokenmacros.h" +#include "basewrapper.h" + +/** + * This is a convenience macro identical to Python's PyObject_TypeCheck, + * except that the arguments have swapped places, for the great convenience + * of generator. + */ +#define SbkObject_TypeCheck(tp, ob) \ + (Py_TYPE(ob) == (tp) || PyType_IsSubtype(Py_TYPE(ob), (tp))) + +extern "C" +{ + +/** + * SbkConverter is used to perform type conversions from C++ + * to Python and vice-versa;.and it is also used for type checking. + * SbkConverter is a private structure that must be accessed + * using the functions provided by the converter API. + */ +struct SbkConverter; + +/** + * Given a void pointer to a C++ object, this function must return + * the proper Python object. It may be either an existing wrapper + * for the C++ object, or a newly create one. Or even the Python + * equivalent of the C++ value passed in the argument. + * + * C++ -> Python + */ +typedef PyObject* (*CppToPythonFunc)(const void*); + +/** + * This function converts a Python object to a C++ value, it may be + * a pointer, value, class, container or primitive type, passed via + * a void pointer, that will be cast properly inside the function. + * This function is usually returned by an IsConvertibleToCppFunc + * function, or obtained knowing the type of the Python object input, + * thus it will not check the Python object type, and will expect + * the void pointer to be pointing to a proper variable. + * + * Python -> C++ + */ +typedef void (*PythonToCppFunc)(PyObject*,void*); + +/** + * Checks if the Python object passed in the argument is convertible to a + * C++ type defined inside the function, it returns the converter function + * that will transform a Python argument into a C++ value. + * It returns NULL if the Python object is not convertible to the C++ type + * that the function represents. + * + * Python -> C++ ? + */ +typedef PythonToCppFunc (*IsConvertibleToCppFunc)(PyObject*); + +} // extern "C" + + +namespace Shiboken { +namespace Conversions { + +/** + * Creates a converter for a wrapper type. + * \param type A Shiboken.ObjectType that will receive the new converter. + * \param toCppPointerConvFunc Function to retrieve the C++ pointer held by a Python wrapper. + * \param toCppPointerCheckFunc Check and return the retriever function of the C++ pointer held by a Python wrapper. + * \param pointerToPythonFunc Function to convert a C++ object to a Python \p type wrapper, keeping their identity. + * \param copyToPythonFunc Function to convert a C++ object to a Python \p type, copying the object. + * \returns The new converter referred by the wrapper \p type. + */ +LIBSHIBOKEN_API SbkConverter* createConverter(SbkObjectType* type, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc = 0); + +LIBSHIBOKEN_API void deleteConverter(SbkConverter* converter); + +/** + * Adds a new conversion of a Python object to a C++ value. + * This is used in copy and implicit conversions. + */ +LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkConverter* converter, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc); +LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkObjectType* type, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc); + +// C++ -> Python --------------------------------------------------------------------------- + +/** + * Retrieves the Python wrapper object for the given \p cppIn C++ pointer object. + * This function is used only for Value and Object Types. + * Example usage: + * TYPE* var; + * PyObject* pyVar = pointerToPython(SBKTYPE, &var); + */ +LIBSHIBOKEN_API PyObject* pointerToPython(SbkObjectType* type, const void* cppIn); + +/** + * For the given \p cppIn C++ reference it returns the Python wrapper object, + * always for Object Types, and when they already exist for reference types; + * for when the latter doesn't have an existing wrapper type, the C++ object + * is copied to Python. + * Example usage: + * TYPE& var = SOMETHING; + * PyObject* pyVar = referenceToPython(SBKTYPE, &var); + */ +LIBSHIBOKEN_API PyObject* referenceToPython(SbkObjectType* type, const void* cppIn); + +/** + * Retrieves the Python wrapper object for the given C++ value pointed by \p cppIn. + * This function is used only for Value Types. + * Example usage: + * TYPE var; + * PyObject* pyVar = copyToPython(SBKTYPE, &var); + */ +LIBSHIBOKEN_API PyObject* copyToPython(SbkObjectType* type, const void* cppIn); +LIBSHIBOKEN_API PyObject* copyToPython(SbkConverter* converter, const void* cppIn); + +// Python -> C++ --------------------------------------------------------------------------- + +/** + * Returns a Python to C++ conversion function if the Python object is convertible to a C++ pointer. + * It returns NULL if the Python object is not convertible to \p type. + */ +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppPointerConvertible(SbkObjectType* type, PyObject* pyIn); + +/** + * Returns a Python to C++ conversion function if the Python object is convertible to a C++ value. + * The resulting converter function will create a copy of the Python object in C++, or implicitly + * convert the object to the expected \p type. + * It returns NULL if the Python object is not convertible to \p type. + */ +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppValueConvertible(SbkObjectType* type, PyObject* pyIn); + +/** + * Returns a Python to C++ conversion function if the Python object is convertible to a C++ reference. + * The resulting converter function will return the underlying C++ object held by the Python wrapper, + * or a new C++ value if it must be a implicit conversion. + * It returns NULL if the Python object is not convertible to \p type. + */ +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppReferenceConvertible(SbkObjectType* type, PyObject* pyIn); + +/// This is the same as isPythonToCppValueConvertible function. +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppConvertible(SbkConverter* converter, PyObject* pyIn); + +/** + * Returns the C++ pointer for the \p pyIn object cast to the type passed via \p desiredType. + * It differs from Shiboken::Object::cppPointer because it casts the pointer to a proper + * memory offset depending on the desired type. + */ +LIBSHIBOKEN_API void* cppPointer(PyTypeObject* desiredType, SbkObject* pyIn); + +/// Converts a Python object \p pyIn to C++ and stores the result in the C++ pointer passed in \p cppOut. +LIBSHIBOKEN_API void pythonToCppPointer(SbkObjectType* type, PyObject* pyIn, void* cppOut); + +/// 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); + +/** + * Helper function returned by generated convertible checking functions + * that returns a C++ NULL when the input Python object is None. + */ +LIBSHIBOKEN_API void nonePythonToCppNullPtr(PyObject*, void* cppOut); + +/** + * Returns true if the \p toCpp function passed is an implicit conversion of Python \p type. + * It is used when C++ expects a reference argument, so it may be the same object received + * from Python, or another created through implicit conversion. + */ +LIBSHIBOKEN_API bool isImplicitConversion(SbkObjectType* type, PythonToCppFunc toCpp); + +} } // namespace Shiboken::Conversions + +#endif // SBK_CONVERTER_H diff --git a/libshiboken/sbkconverter_p.h b/libshiboken/sbkconverter_p.h new file mode 100644 index 00000000..199538cc --- /dev/null +++ b/libshiboken/sbkconverter_p.h @@ -0,0 +1,90 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team <contact@pyside.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SBK_CONVERTER_P_H +#define SBK_CONVERTER_P_H + +#include <Python.h> +#include <list> +#include "sbkconverter.h" + +extern "C" +{ + +typedef std::pair<IsConvertibleToCppFunc, PythonToCppFunc> ToCppConversion; +typedef std::list<ToCppConversion> ToCppConversionList; + +/** + * \internal + * Private structure of SbkConverter. + */ +struct SbkConverter +{ + /** + * TODO: it certainly will be empty in some cases, like with PyDate. + * TODO: probably a setPythonType(SbkConverter*, PyTypeObject*) function will be required. + * Python type associated with this converter. If the type is a Shiboken + * wrapper, then it must be a SbkObjectType; otherwise it will be the + * Python type to which the C++ value will be converted (note that the + * C++ type could be produced from various Python types). + */ + PyTypeObject* pythonType; + /** + * This function converts a C++ object to a Python object of the type + * indicated in pythonType. The identity of the C++ object is kept, + * because it looks for an already existing Python wrapper associated + * with the C++ instance. + * It is used to convert C++ pointers and references to Python objects. + */ + CppToPythonFunc pointerToPython; + /** + * This function converts a C++ object to a Python object of the type + * indicated in pythonType. The identity of the is not kept, because a + * new instance of the C++ object is created. + * It is used to convert objects passed by value, or reference, if said + * reference can't be traced to an object that already has a Python + * wrapper assigned for it. + */ + CppToPythonFunc copyToPython; + /** + * This is a special case of a Python to C++ conversion. It returns + * the underlying C++ pointer of a Python wrapper passed as parameter + * or NULL if the Python object is a None value. + * It comes separated from the other toCppConversions because if you + * have a Python object representing a Value Type the type checking + * for both ValueType and ValueType* would be the same, thus the first + * check would be true and the second would never be reached. + */ + ToCppConversion toCppPointerConversion; + /** + * This is a list of type checking functions that return the + * proper Python to C++ conversion function, for the given Python + * object. + * For Object Types, that never have implicit conversions, this + * list is always empty. + */ + ToCppConversionList toCppConversions; +}; + +} // extern "C" + +#endif // SBK_CONVERTER_P_H diff --git a/libshiboken/shiboken.h b/libshiboken/shiboken.h index 269f01c9..274c1ce1 100644 --- a/libshiboken/shiboken.h +++ b/libshiboken/shiboken.h @@ -31,6 +31,7 @@ #include "gilstate.h" #include "threadstatesaver.h" #include "helper.h" +#include "sbkconverter.h" #include "sbkenum.h" #include "sbkmodule.h" #include "sbkstring.h" diff --git a/tests/samplebinding/injectcode_test.py b/tests/samplebinding/injectcode_test.py index 2941b923..a60ef914 100644 --- a/tests/samplebinding/injectcode_test.py +++ b/tests/samplebinding/injectcode_test.py @@ -90,6 +90,19 @@ class InjectCodeTest(unittest.TestCase): result = ic.callArrayMethod(values) self.assertEqual(result, ic.multiplier * sum(values)) + def testUsageOfTypeSystemCheckVariableOnPrimitiveType(self): + '''When the sequence item is convertible to an integer -1 is returned, or -2 if its not convertible.''' + ic = InjectCode() + values = (1, 2, 3, 4, '5', 6.7) + print values + result = ic.arrayMethod(values) + print result + fixedValues = [v for v in values if isinstance(v, int)]\ + + [-1 for v in values if isinstance(v, float)]\ + + [-2 for v in values if not isinstance(v, int) and not isinstance(v, float)] + print fixedValues + #self.assertEqual(result, sum(fixedValues)) + class IntArrayTest(unittest.TestCase): '''Test case for converting python sequence to int array''' diff --git a/tests/samplebinding/typesystem_sample.xml b/tests/samplebinding/typesystem_sample.xml index bced56f8..12245618 100644 --- a/tests/samplebinding/typesystem_sample.xml +++ b/tests/samplebinding/typesystem_sample.xml @@ -203,7 +203,8 @@ <replace-type modified-type="str"/> </modify-argument> <inject-code class='target' position='beginning'> - %0 = new %FUNCTION_NAME(atoi(%CONVERTTOCPP[const char*](%PYARG_1))); + const char* tmpArg = %CONVERTTOCPP[const char*](%PYARG_1); + %0 = new %FUNCTION_NAME(atoi(tmpArg)); </inject-code> </modify-function> </object-type> @@ -246,7 +247,18 @@ <inject-code class="native" position="beginning"> static void reparent_layout_items(PyObject* parent, PyObject* layout) { - const ObjectTypeList& objChildren = %CONVERTTOCPP[ObjectTypeLayout*](layout)->objects(); + // CHECKTYPE and ISCONVERTIBLE are used here for test purposes, don't change them. + if (!%CHECKTYPE[ObjectTypeLayout*](layout) && !%ISCONVERTIBLE[ObjectTypeLayout*](layout)) + return; + // %CHECKTYPE[ObjectTypeLayout*](layout) + // %ISCONVERTIBLE[ObjectTypeLayout*](layout) + ObjectTypeLayout* var; + var = %CONVERTTOCPP[ObjectTypeLayout*](layout); + // TODO-CONVERTER: erase this + /* + ObjectTypeLayout* var2 = %CONVERTTOCPP[ObjectTypeLayout*](layout); + */ + const ObjectTypeList& objChildren = var->objects(); ObjectTypeList::const_iterator it = objChildren.begin(); for (; it != objChildren.end(); ++it) { if ((*it)->isLayoutType()) { @@ -1083,8 +1095,14 @@ <conversion-rule class="native"> int numItems = PySequence_Size(%PYARG_1); Shiboken::AutoArrayPointer<int> %out(numItems); - for (int i = 0; i < numItems; ++i) - %out[i] = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(%PYARG_1, i)); + for (int i = 0; i < numItems; ++i) { + if (%CHECKTYPE[int](PySequence_Fast_GET_ITEM(%PYARG_1, i))) + %out[i] = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(%PYARG_1, i)); + else if (%ISCONVERTIBLE[int](PySequence_Fast_GET_ITEM(%PYARG_1, i))) + %out[i] = -1; + else + %out[i] = -2; + } </conversion-rule> <conversion-rule class="target"> PyObject* %out = PyList_New(count); @@ -1539,7 +1557,17 @@ </value-type> <value-type name="ByteArray" hash-function="ByteArray::hash"> - <conversion-rule file="bytearray_conversions.h"/> + <conversion-rule file="bytearray_conversions.h"> + <target-to-native> + <add-conversion type='Py_None' check='%in == Py_None'> + %out = %OUTTYPE(); + </add-conversion> + <add-conversion type='PyString' check='PyString_Check(%in)'> + %out = %OUTTYPE(PyString_AS_STRING(%in), PyString_GET_SIZE(%in)); + </add-conversion> + </target-to-native> + </conversion-rule> + <modify-function signature="ByteArray(const char*,int)" remove="all" /> <modify-function signature="ByteArray(const char*)"> <!-- Keep \x00 bytes passed in Python strings. --> @@ -1774,7 +1802,30 @@ <extra-includes> <include file-name="datetime.h" location="global"/> </extra-includes> - <conversion-rule class="target" file="date_conversions.h"/> + <inject-code class="native" position="beginning"> + static bool PyDate_ImportAndCheck(PyObject* pyIn) { + if (!PyDateTimeAPI) + PyDateTime_IMPORT; + return PyDate_Check(pyIn); + } + </inject-code> + <conversion-rule class="target" file="date_conversions.h"> + <target-to-native> + <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); + %out = %OUTTYPE(day, month, year); + </add-conversion> + </target-to-native> + </conversion-rule> + <add-function signature="toPython()" return-type="PyDate"> + <inject-code class="target"> + if (!PyDateTimeAPI) + PyDateTime_IMPORT; + %PYARG_0 = PyDate_FromDate(%CPPSELF.day(), %CPPSELF.month(), %CPPSELF.year()); + </inject-code> + </add-function> </value-type> <object-type name="HandleHolder" /> |