summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcelo Lira <marcelo.lira@openbossa.org>2011-11-04 20:01:33 -0300
committerMarcelo Lira <marcelo.lira@openbossa.org>2011-12-09 20:28:08 -0300
commitd25db2975162f592892313e10938531d8505e6c4 (patch)
tree4c52028ada45453ec98d25e15b497a7921dd34f0
parent741142d29716de26b1630aca70d93227811ab579 (diff)
downloadshiboken-d25db2975162f592892313e10938531d8505e6c4.tar.gz
shiboken-d25db2975162f592892313e10938531d8505e6c4.tar.xz
shiboken-d25db2975162f592892313e10938531d8505e6c4.zip
Added adapter class SpecificConverter to provide type conversion based on a given string.
Also added code to register a couple of type conversions by name, a bunch of related tests, and some fixes to the converter functions.
-rw-r--r--generator/cppgenerator.cpp41
-rw-r--r--generator/shibokengenerator.cpp4
-rw-r--r--libshiboken/sbkconverter.cpp63
-rw-r--r--libshiboken/sbkconverter.h33
-rw-r--r--tests/samplebinding/typeconverters_test.py70
-rw-r--r--tests/samplebinding/typesystem_sample.xml61
6 files changed, 243 insertions, 29 deletions
diff --git a/generator/cppgenerator.cpp b/generator/cppgenerator.cpp
index bc84860a..3eac8d05 100644
--- a/generator/cppgenerator.cpp
+++ b/generator/cppgenerator.cpp
@@ -1194,11 +1194,15 @@ void CppGenerator::writeConverterRegister(QTextStream& s, const AbstractMetaClas
s << endl;
- s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"";
- s << metaClass->qualifiedCppName() << "\");" << endl;
- // TODO-CONVERTER: review the need for registering this pointer version of the type name.
- s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"";
- s << metaClass->qualifiedCppName() << "*\");" << endl;
+ QStringList cppSignature = metaClass->qualifiedCppName().split("::", QString::SkipEmptyParts);
+ while (!cppSignature.isEmpty()) {
+ QString signature = cppSignature.join("::");
+ s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "\");" << endl;
+ s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "*\");" << endl;
+ s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "&\");" << endl;
+ cppSignature.removeFirst();
+ }
+
s << INDENT << "Shiboken::Conversions::registerConverterName(converter, typeid(::";
s << metaClass->qualifiedCppName() << ").name());" << endl;
if (shouldGenerateCppWrapper(metaClass)) {
@@ -3227,7 +3231,12 @@ void CppGenerator::writeEnumConverterInitialization(QTextStream& s, const TypeEn
s << INDENT << "Shiboken::Enum::setTypeConverter(" << enumPythonType << ", converter);" << endl;
s << INDENT << "Shiboken::Enum::setTypeConverter(" << enumPythonType << ", converter);" << endl;
- s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << enumType->qualifiedCppName() << "\");" << endl;
+ QStringList cppSignature = enumType->qualifiedCppName().split("::", QString::SkipEmptyParts);
+ while (!cppSignature.isEmpty()) {
+ QString signature = cppSignature.join("::");
+ s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "\");" << endl;
+ cppSignature.removeFirst();
+ }
}
s << INDENT << '}' << endl;
@@ -3237,7 +3246,8 @@ void CppGenerator::writeEnumConverterInitialization(QTextStream& s, const TypeEn
void CppGenerator::writeContainerConverterInitialization(QTextStream& s, const AbstractMetaType* type)
{
- s << INDENT << "// Register converter for type '" << type->cppSignature() << "'." << endl;
+ QByteArray cppSignature = QMetaObject::normalizedSignature(type->cppSignature().toAscii());
+ s << INDENT << "// Register converter for type '" << cppSignature << "'." << endl;
QString converter = converterObject(type);
s << INDENT << converter << " = Shiboken::Conversions::createConverter(";
if (type->typeEntry()->targetLangApiName() == "PyObject") {
@@ -3252,7 +3262,12 @@ void CppGenerator::writeContainerConverterInitialization(QTextStream& s, const A
s << ", " << cppToPythonFunctionName(typeName, typeName) << ");" << endl;
QString toCpp = pythonToCppFunctionName(typeName, typeName);
QString isConv = convertibleToCppFunctionName(typeName, typeName);
- s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << type->cppSignature() << "\");" << endl;
+ s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");" << endl;
+ if (usePySideExtensions() && cppSignature.startsWith("const ") && cppSignature.endsWith("&")) {
+ cppSignature.chop(1);
+ cppSignature.remove(0, sizeof("const ") / sizeof(char) - 1);
+ s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");" << endl;
+ }
writeAddPythonToCppConversion(s, converterObject(type), toCpp, isConv);
}
@@ -4744,7 +4759,6 @@ void CppGenerator::finishGeneration()
delete targetType;
}
}
- //s << endl;
}
QList<const CustomConversion*> typeConversions = getPrimitiveCustomConversions();
@@ -4875,8 +4889,13 @@ void CppGenerator::finishGeneration()
const TypeEntry* alias = pte->basicAliasedTypeEntry();
if (!alias)
continue;
- s << INDENT << "Shiboken::Conversions::registerConverterName(";
- s << converterObject(alias) << ", \"" << pte->qualifiedCppName() << "\");" << endl;
+ QString converter = converterObject(alias);
+ QStringList cppSignature = pte->qualifiedCppName().split("::", QString::SkipEmptyParts);
+ while (!cppSignature.isEmpty()) {
+ QString signature = cppSignature.join("::");
+ s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << signature << "\");" << endl;
+ cppSignature.removeFirst();
+ }
}
// Register type resolver for all containers found in signals.
QSet<QByteArray> typeResolvers;
diff --git a/generator/shibokengenerator.cpp b/generator/shibokengenerator.cpp
index c1d124a8..a239e757 100644
--- a/generator/shibokengenerator.cpp
+++ b/generator/shibokengenerator.cpp
@@ -1030,6 +1030,8 @@ QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry* type, bool gene
QString typeCheck;
if (type->targetLangApiName() == type->name())
typeCheck = cpythonIsConvertibleFunction(type);
+ else if (type->targetLangApiName() == "PyUnicode")
+ typeCheck = "Shiboken::String::check";
else
typeCheck = QString("%1_Check").arg(type->targetLangApiName());
return typeCheck;
@@ -1125,7 +1127,7 @@ QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaT
{
if (isWrapperType(type)) {
QString conversion;
- if (type->isReference() && !isPointer(type))
+ if (type->isReference() && !(type->isValue() && type->isConstant()) && !isPointer(type))
conversion = "reference";
else if (type->isValue())
conversion = "copy";
diff --git a/libshiboken/sbkconverter.cpp b/libshiboken/sbkconverter.cpp
index ef332831..edd7097d 100644
--- a/libshiboken/sbkconverter.cpp
+++ b/libshiboken/sbkconverter.cpp
@@ -172,17 +172,12 @@ PyObject* referenceToPython(SbkConverter* converter, const void* cppIn)
{
assert(cppIn);
- // If it is a Object Type, produce a wrapper for it.
- if (!converter->copyToPython)
- return 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 converter->copyToPython(cppIn);
+ return converter->pointerToPython(cppIn);
}
static inline PyObject* CopyCppToPython(SbkConverter* converter, const void* cppIn)
@@ -245,6 +240,8 @@ void nonePythonToCppNullPtr(PyObject*, void* cppOut)
void* cppPointer(PyTypeObject* desiredType, SbkObject* pyIn)
{
assert(pyIn);
+ if (!ObjectType::checkType(desiredType))
+ return pyIn;
SbkObjectType* inType = (SbkObjectType*)Py_TYPE(pyIn);
if (ObjectType::hasCast(inType))
return ObjectType::cast(inType, pyIn, desiredType);
@@ -314,15 +311,12 @@ bool isImplicitConversion(SbkObjectType* type, PythonToCppFunc toCppFunc)
void registerConverterName(SbkConverter* converter , const char* typeName)
{
ConvertersMap::iterator iter = converters.find(typeName);
- if (iter == converters.end()) {
- //SbkDbg() << "Registering " << typeName;
+ if (iter == converters.end())
converters.insert(std::make_pair(typeName, converter));
- }
}
SbkConverter* getConverter(const char* typeName)
{
- //SbkDbg() << "Looking for converter for type " << typeName;
ConvertersMap::const_iterator it = converters.find(typeName);
if (it != converters.end())
return it->second;
@@ -480,4 +474,53 @@ bool pythonTypeIsObjectType(SbkConverter* converter)
return converter->pointerToPython && !converter->copyToPython;
}
+SpecificConverter::SpecificConverter(const char* typeName)
+ : m_type(InvalidConversion)
+{
+ m_converter = getConverter(typeName);
+ if (!m_converter)
+ return;
+ int len = strlen(typeName);
+ char lastChar = typeName[len -1];
+ if (lastChar == '&') {
+ m_type = ReferenceConversion;
+ } else if (lastChar == '*' || pythonTypeIsObjectType(m_converter)) {
+ m_type = PointerConversion;
+ } else {
+ m_type = CopyConversion;
+ }
+}
+
+PyObject* SpecificConverter::toPython(const void* cppIn)
+{
+ switch (m_type) {
+ case CopyConversion:
+ return copyToPython(m_converter, cppIn);
+ case PointerConversion:
+ return pointerToPython(m_converter, *((const void**)cppIn));
+ case ReferenceConversion:
+ return referenceToPython(m_converter, cppIn);
+ default:
+ PyErr_SetString(PyExc_RuntimeError, "tried to use invalid converter in 'C++ to Python' conversion");
+ }
+ return 0;
+}
+
+void SpecificConverter::toCpp(PyObject* pyIn, void* cppOut)
+{
+ switch (m_type) {
+ case CopyConversion:
+ pythonToCppCopy(m_converter, pyIn, cppOut);
+ break;
+ case PointerConversion:
+ pythonToCppPointer(m_converter, pyIn, cppOut);
+ break;
+ case ReferenceConversion:
+ pythonToCppPointer(m_converter, pyIn, &cppOut);
+ break;
+ default:
+ PyErr_SetString(PyExc_RuntimeError, "tried to use invalid converter in 'Python to C++' conversion");
+ }
+}
+
} } // namespace Shiboken::Conversions
diff --git a/libshiboken/sbkconverter.h b/libshiboken/sbkconverter.h
index a5b8d113..41126786 100644
--- a/libshiboken/sbkconverter.h
+++ b/libshiboken/sbkconverter.h
@@ -36,8 +36,6 @@
#define SbkObject_TypeCheck(tp, ob) \
(Py_TYPE(ob) == (tp) || PyType_IsSubtype(Py_TYPE(ob), (tp)))
-#define SbkString_Check(pyObj) (pyObj == Py_None || PyString_Check(pyObj))
-
extern "C"
{
@@ -88,6 +86,36 @@ typedef PythonToCppFunc (*IsConvertibleToCppFunc)(PyObject*);
namespace Shiboken {
namespace Conversions {
+
+class LIBSHIBOKEN_API SpecificConverter
+{
+public:
+ enum Type
+ {
+ InvalidConversion,
+ CopyConversion,
+ PointerConversion,
+ ReferenceConversion
+ };
+
+ explicit SpecificConverter(const char* typeName);
+
+ inline SbkConverter* converter() { return m_converter; }
+ inline operator SbkConverter*() const { return m_converter; }
+
+ inline bool isValid() { return m_type != InvalidConversion; }
+ inline operator bool() const { return m_type != InvalidConversion; }
+
+ inline Type conversionType() { return m_type; }
+
+ PyObject* toPython(const void* cppIn);
+ void toCpp(PyObject* pyIn, void* cppOut);
+private:
+ SbkConverter* m_converter;
+ Type m_type;
+};
+
+
/**
* Creates a converter for a wrapper type.
* \param type A Shiboken.ObjectType that will receive the new converter.
@@ -306,4 +334,5 @@ template<> inline SbkConverter* PrimitiveTypeConverter<void*>() { return primiti
struct _SbkGenericType { PyHeapTypeObject super; SbkConverter** converter; };
#define SBK_CONVERTER(pyType) (*reinterpret_cast<_SbkGenericType*>(pyType)->converter)
+
#endif // SBK_CONVERTER_H
diff --git a/tests/samplebinding/typeconverters_test.py b/tests/samplebinding/typeconverters_test.py
index 4261030a..6b40573e 100644
--- a/tests/samplebinding/typeconverters_test.py
+++ b/tests/samplebinding/typeconverters_test.py
@@ -65,15 +65,15 @@ class GetPythonTypeByNameTest(unittest.TestCase):
self.assertEqual(pyType, pyTypedef)
def testPairContainerType(self):
- pyType = sample.getPythonType('std::pair<Complex, int >')
+ pyType = sample.getPythonType('std::pair<Complex,int>')
self.assertEqual(pyType, list)
def testListContainerType(self):
- pyType = sample.getPythonType('std::list<int >')
+ pyType = sample.getPythonType('std::list<int>')
self.assertEqual(pyType, list)
def testMapContainerType(self):
- pyType = sample.getPythonType('std::map<std::string, int >')
+ pyType = sample.getPythonType('std::map<std::string,int>')
self.assertEqual(pyType, dict)
def testGlobalEnumType(self):
@@ -116,8 +116,68 @@ class CheckValueAndObjectTypeByNameTest(unittest.TestCase):
self.assertFalse(sample.cppTypeIsObjectType('Complex'))
def testContainerType(self):
- self.assertFalse(sample.cppTypeIsValueType('std::list<int >'))
- self.assertFalse(sample.cppTypeIsObjectType('std::list<int >'))
+ self.assertFalse(sample.cppTypeIsValueType('std::list<int>'))
+ self.assertFalse(sample.cppTypeIsObjectType('std::list<int>'))
+
+
+class SpecificConverterTest(unittest.TestCase):
+
+ '''Uses an added function with inject code that uses the libshiboken
+ adapter class "Shiboken::Conversions::SpecificConverter".'''
+
+ def testNotExistentType(self):
+ conversion = sample.getConversionTypeString('NotExistentType')
+ self.assertEqual(conversion, 'Invalid conversion')
+
+ def testObjectType(self):
+ conversion = sample.getConversionTypeString('ObjectType')
+ self.assertEqual(conversion, 'Pointer conversion')
+ conversion = sample.getConversionTypeString('ObjectType*')
+ self.assertEqual(conversion, 'Pointer conversion')
+ conversion = sample.getConversionTypeString('ObjectType&')
+ self.assertEqual(conversion, 'Reference conversion')
+
+ def testValueType(self):
+ conversion = sample.getConversionTypeString('Point')
+ self.assertEqual(conversion, 'Copy conversion')
+ conversion = sample.getConversionTypeString('Point*')
+ self.assertEqual(conversion, 'Pointer conversion')
+ conversion = sample.getConversionTypeString('Point&')
+ self.assertEqual(conversion, 'Reference conversion')
+
+
+class StringBasedConversionTest(unittest.TestCase):
+
+ def testValueType(self):
+ pts = (sample.Point(1, 1), sample.Point(2, 2), sample.Point(3, 3))
+ result = sample.convertValueTypeToCppAndThenToPython(pts[0], pts[1], pts[2])
+ for orig, new in zip(pts, result):
+ self.assertEqual(orig, new)
+ self.assertFalse(pts[0] is result[0])
+ self.assertTrue(pts[1] is result[1])
+ self.assertTrue(pts[2] is result[2])
+
+ def testObjectType(self):
+ objs = (sample.ObjectType(), sample.ObjectType())
+ objs[0].setObjectName('obj0')
+ objs[1].setObjectName('obj1')
+ result = sample.convertObjectTypeToCppAndThenToPython(objs[0], objs[1])
+ for orig, new in zip(objs, result):
+ self.assertEqual(orig, new)
+ self.assertEqual(orig.objectName(), new.objectName())
+ self.assertTrue(orig is new)
+
+ def testContainerType(self):
+ lst = range(4)
+ result = sample.convertListOfIntegersToCppAndThenToPython(lst)
+ self.assertTrue(len(result), 1)
+ self.assertTrue(lst, result[0])
+
+ def testCppPrimitiveType(self):
+ integers = (12, 34)
+ result = sample.convertIntegersToCppAndThenToPython(integers[0], integers[1])
+ for orig, new in zip(integers, result):
+ self.assertEqual(orig, new)
if __name__ == '__main__':
diff --git a/tests/samplebinding/typesystem_sample.xml b/tests/samplebinding/typesystem_sample.xml
index 0c5ee7cd..b2b795aa 100644
--- a/tests/samplebinding/typesystem_sample.xml
+++ b/tests/samplebinding/typesystem_sample.xml
@@ -285,6 +285,67 @@
</inject-code>
</add-function>
+ <add-function signature="getConversionTypeString(const char*)" return-type="PyObject">
+ <inject-code class="target" position="beginning">
+ Shiboken::Conversions::SpecificConverter converter(%1);
+ const char* %0 = 0;
+ switch (converter.conversionType()) {
+ case Shiboken::Conversions::SpecificConverter::CopyConversion:
+ %0 = "Copy conversion";
+ break;
+ case Shiboken::Conversions::SpecificConverter::PointerConversion:
+ %0 = "Pointer conversion";
+ break;
+ case Shiboken::Conversions::SpecificConverter::ReferenceConversion:
+ %0 = "Reference conversion";
+ break;
+ default:
+ %0 = "Invalid conversion";
+ }
+ %PYARG_0 = %CONVERTTOPYTHON[const char*](%0);
+ </inject-code>
+ </add-function>
+
+ <inject-code class="native" position="beginning">
+ static PyObject* __convertCppValuesToPython(const char** typeName, void** values, int size)
+ {
+ PyObject* result = PyTuple_New(size);
+ for (int i = 0; i &lt; size; ++i) {
+ Shiboken::Conversions::SpecificConverter converter(typeName[i]);
+ PyTuple_SET_ITEM(result, i, converter.toPython(values[i]));
+ }
+ return result;
+ }
+ </inject-code>
+ <add-function signature="convertValueTypeToCppAndThenToPython(Point,Point*,Point&amp;)" return-type="PyObject">
+ <inject-code class="target" position="beginning">
+ const char* typeNames[] = { "Point", "Point*", "Point&amp;" };
+ void* values[] = { &amp;%1, &amp;%2, &amp;(%3) };
+ %PYARG_0 = __convertCppValuesToPython(typeNames, values, 3);
+ </inject-code>
+ </add-function>
+ <add-function signature="convertObjectTypeToCppAndThenToPython(ObjectType*,ObjectType&amp;)" return-type="PyObject">
+ <inject-code class="target" position="beginning">
+ const char* typeNames[] = { "ObjectType*", "ObjectType&amp;" };
+ void* values[] = { &amp;%1, &amp;(%2) };
+ %PYARG_0 = __convertCppValuesToPython(typeNames, values, 2);
+ </inject-code>
+ </add-function>
+ <add-function signature="convertListOfIntegersToCppAndThenToPython(std::list&lt;int&gt;)" return-type="PyObject">
+ <inject-code class="target" position="beginning">
+ const char* typeNames[] = { "std::list&lt;int&gt;" };
+ void* values[] = { &amp;%1 };
+ %PYARG_0 = __convertCppValuesToPython(typeNames, values, 1);
+ </inject-code>
+ </add-function>
+ <add-function signature="convertIntegersToCppAndThenToPython(int,int)" return-type="PyObject">
+ <inject-code class="target" position="beginning">
+ const char* typeNames[] = { "int", "int" };
+ void* values[] = { &amp;%1, &amp;%2 };
+ %PYARG_0 = __convertCppValuesToPython(typeNames, values, 2);
+ </inject-code>
+ </add-function>
+
<container-type name="std::pair" type="pair">
<include file-name="utility" location="global"/>
<conversion-rule file="pair_conversions.h">