diff options
author | Hugo Lima <hugo.lima@openbossa.org> | 2010-01-14 15:18:19 -0200 |
---|---|---|
committer | Hugo Lima <hugo.lima@openbossa.org> | 2010-01-14 16:40:49 -0200 |
commit | 67f0c4988020833210537757fbd51ecb84825ce8 (patch) | |
tree | 805db73764d91ab3d6e9ea8d87b51207eeb343ca | |
parent | c40f61ff076928f07591070763069cc75918768e (diff) |
Implemented type discovery feature.
The problem:
- There are two class, A and B, B inherits from A.
- You are inside a virtual method reimplemented in python with just one parameter of type A*.
- But the object referenced by a variable of type A* is an instance of B and it was created by C++,
not Python!
- Shiboken needs to create a PyObject of type B, not A! This does not makes sense for C++, but does
for Python, because python variables does not store type information, just values.
To achieve this we use RTTI to get the real type name of a variable, then we create the PyObject using
the TypeResolver infrastructure initially developed to help with signal slot problems. In other words,
the TypeResolver class has been moved from libpyside to libshiboken.
-rw-r--r-- | cppgenerator.cpp | 32 | ||||
-rw-r--r-- | cppgenerator.h | 1 | ||||
-rw-r--r-- | libshiboken/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libshiboken/basewrapper.cpp | 20 | ||||
-rw-r--r-- | libshiboken/basewrapper.h | 4 | ||||
-rw-r--r-- | libshiboken/conversions.h | 8 | ||||
-rw-r--r-- | libshiboken/typeresolver.cpp | 125 | ||||
-rw-r--r-- | libshiboken/typeresolver.h | 113 |
8 files changed, 291 insertions, 13 deletions
diff --git a/cppgenerator.cpp b/cppgenerator.cpp index 3f83f137e..f6bae1c19 100644 --- a/cppgenerator.cpp +++ b/cppgenerator.cpp @@ -90,8 +90,9 @@ void CppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *metaCl // headers s << "// default includes" << endl; s << "#include <shiboken.h>" << endl; + s << "#include <typeresolver.h>\n"; + s << "#include <typeinfo>\n"; if (usePySideExtensions()) { - s << "#include <typeresolver.h>\n"; if (metaClass->isQObject()) { s << "#include <signalmanager.h>\n"; s << "#include <dynamicqmetaobject.h>\n"; @@ -146,6 +147,9 @@ void CppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *metaCl s << endl; } + if (metaClass->isPolymorphic()) + writeTypeNameFunction(s, metaClass); + if (shouldGenerateCppWrapper(metaClass)) { s << "// Native ---------------------------------------------------------" << endl; s << endl; @@ -1390,6 +1394,7 @@ void CppGenerator::writeClassDefinition(QTextStream& s, const AbstractMetaClass* QString tp_as_number('0'); QString tp_as_sequence('0'); QString mi_init('0'); + QString type_name_func('0'); QString mi_specialcast('0'); QString cppClassName = metaClass->qualifiedCppName(); QString className = cpythonTypeName(metaClass).replace(QRegExp("_Type$"), ""); @@ -1429,6 +1434,9 @@ void CppGenerator::writeClassDefinition(QTextStream& s, const AbstractMetaClass* tp_init = ctors.isEmpty() ? "0" : QString("(initproc)%1_Init").arg(className); } + if (metaClass->isPolymorphic()) + type_name_func = cpythonBaseName(metaClass->typeEntry()) + "_typeName"; + if (metaClass->hasPrivateDestructor()) tp_new = "0"; else @@ -1513,7 +1521,8 @@ void CppGenerator::writeClassDefinition(QTextStream& s, const AbstractMetaClass* s << "}, }," << endl; s << INDENT << "/*mi_offsets*/ 0," << endl; s << INDENT << "/*mi_init*/ " << mi_init << ',' << endl; - s << INDENT << "/*mi_specialcast*/ " << mi_specialcast << endl; + s << INDENT << "/*mi_specialcast*/ " << mi_specialcast << ',' << endl; + s << INDENT << "/*type_name_func*/ " << type_name_func << endl; s << "};" << endl; } @@ -2181,11 +2190,15 @@ void CppGenerator::writeClassRegister(QTextStream& s, const AbstractMetaClass* m writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::End, TypeSystem::TargetLangCode, 0, 0, metaClass); } - if (usePySideExtensions() && !metaClass->isNamespace()) { + if (!metaClass->isNamespace()) { bool isObjectType = metaClass->typeEntry()->isObject(); - QString typeName = metaClass->qualifiedCppName() + (isObjectType ? "*" : ""); - s << INDENT << "PySide::TypeResolver::create" << (isObjectType ? "Object" : "Value"); - s << "TypeResolver<" << typeName << " >" << "(\"" << typeName << "\");\n"; + QString typeName = metaClass->qualifiedCppName(); + QString registeredTypeName = typeName + (isObjectType ? "*" : ""); + QString functionSufix = isObjectType ? "Object" : "Value"; + s << INDENT << "Shiboken::TypeResolver::create" << functionSufix; + s << "TypeResolver<" << typeName << " >" << "(\"" << registeredTypeName << "\");\n"; + s << INDENT << "Shiboken::TypeResolver::create" << functionSufix; + s << "TypeResolver<" << typeName << " >" << "(typeid(" << typeName << ").name());\n"; } s << '}' << endl << endl; @@ -2258,6 +2271,13 @@ void CppGenerator::writeTypeConverterImpl(QTextStream& s, const TypeEntry* type) s << INDENT << "return *" << cpythonWrapperCPtr(type, "pyobj") << ';' << endl; s << '}' << endl << endl; } +void CppGenerator::writeTypeNameFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + Indentation indent(INDENT); + s << "static const char* " << cpythonBaseName(metaClass->typeEntry()) << "_typeName(const void* cptr)\n{\n"; + s << INDENT << "return typeid(*reinterpret_cast<const " << metaClass->qualifiedCppName() << "*>(cptr)).name();\n"; + s << "}\n\n"; +} void CppGenerator::writeSbkCopyCppObjectFunction(QTextStream& s, const AbstractMetaClass* metaClass) { diff --git a/cppgenerator.h b/cppgenerator.h index 132a075cd..15e0a05fc 100644 --- a/cppgenerator.h +++ b/cppgenerator.h @@ -65,6 +65,7 @@ private: void writeTypeCheck(QTextStream& s, const OverloadData* overloadData, QString argumentName); void writeTypeConverterImpl(QTextStream& s, const TypeEntry* type); + void writeTypeNameFunction(QTextStream& s, const AbstractMetaClass* metaClass); void writeSbkCopyCppObjectFunction(QTextStream& s, const AbstractMetaClass* metaClass); diff --git a/libshiboken/CMakeLists.txt b/libshiboken/CMakeLists.txt index f36567d63..3cc0a5618 100644 --- a/libshiboken/CMakeLists.txt +++ b/libshiboken/CMakeLists.txt @@ -14,6 +14,7 @@ basewrapper.cpp helper.cpp pyenum.cpp bindingmanager.cpp +typeresolver.cpp ) include_directories(${CMAKE_CURRENT_SOURCE_DIR} diff --git a/libshiboken/basewrapper.cpp b/libshiboken/basewrapper.cpp index cad4efe0d..b0f1b9c0a 100644 --- a/libshiboken/basewrapper.cpp +++ b/libshiboken/basewrapper.cpp @@ -36,6 +36,7 @@ #include <cstddef> #include <algorithm> #include "autodecref.h" +#include "typeresolver.h" namespace Shiboken { @@ -109,12 +110,24 @@ void destroyParentInfo(SbkBaseWrapper* obj, bool removeFromParent) PyObject* SbkBaseWrapper_New(SbkBaseWrapperType* instanceType, const void* cptr, bool hasOwnership, - bool containsCppWrapper) + bool isExactType) { + // Try to find the exact type of cptr. + if (!isExactType && instanceType->type_name_func) { + const char* typeName = instanceType->type_name_func(cptr); + TypeResolver* typeResolver = TypeResolver::get(typeName); + if (typeResolver) + instanceType = reinterpret_cast<SbkBaseWrapperType*>(typeResolver->pythonType()); + #ifndef NDEBUG + else + fprintf(stderr, "[SHIBOKEN] Can't find type resolver for %s.\n", typeName); + #endif + } + SbkBaseWrapper* self = reinterpret_cast<SbkBaseWrapper*>(SbkBaseWrapper_TpNew(reinterpret_cast<PyTypeObject*>(instanceType), 0, 0)); self->cptr = const_cast<void*>(cptr); self->hasOwnership = hasOwnership; - self->containsCppWrapper = containsCppWrapper; + self->containsCppWrapper = 0; self->validCppObject = 1; self->parentInfo = 0; BindingManager::instance().registerWrapper(self); @@ -253,7 +266,8 @@ SbkBaseWrapperType SbkBaseWrapper_Type = { { { }, }, /*mi_offsets*/ 0, /*mi_init*/ 0, -/*mi_specialcast*/ 0 +/*mi_specialcast*/ 0, +/*type_name_func*/ 0 }; PyAPI_FUNC(void) init_shiboken() diff --git a/libshiboken/basewrapper.h b/libshiboken/basewrapper.h index 7de5f9086..38e0a0c48 100644 --- a/libshiboken/basewrapper.h +++ b/libshiboken/basewrapper.h @@ -69,6 +69,7 @@ struct SbkBaseWrapperType; * The implementation of this function is auto generated by the generator and you don't need to care about it. */ typedef void* (*SpecialCastFunction)(PyObject*, SbkBaseWrapperType*); +typedef const char* (*TypeNameFunction)(const void*); LIBSHIBOKEN_API PyAPI_DATA(PyTypeObject) SbkBaseWrapperType_Type; LIBSHIBOKEN_API PyAPI_DATA(SbkBaseWrapperType) SbkBaseWrapper_Type; @@ -81,6 +82,7 @@ struct LIBSHIBOKEN_API SbkBaseWrapperType MultipleInheritanceInitFunction mi_init; /// Special cast function, null if this class doesn't have multiple inheritance. SpecialCastFunction mi_specialcast; + TypeNameFunction type_name_func; }; /// Base Python object for all the wrapped C++ classes. @@ -194,7 +196,7 @@ LIBSHIBOKEN_API PyAPI_FUNC(PyObject*) SbkBaseWrapper_New(SbkBaseWrapperType* instanceType, const void* cptr, bool hasOwnership = true, - bool containsCppWrapper = false); + bool isExactType = false); LIBSHIBOKEN_API PyAPI_FUNC(PyObject*) SbkBaseWrapper_TpNew(PyTypeObject* subtype, PyObject*, PyObject*); diff --git a/libshiboken/conversions.h b/libshiboken/conversions.h index 1dc00b00f..e3b2ac34b 100644 --- a/libshiboken/conversions.h +++ b/libshiboken/conversions.h @@ -89,10 +89,10 @@ struct CppObjectCopier * Convenience template to create wrappers using the proper Python type for a given C++ class instance. */ template<typename T> -inline PyObject* SbkCreateWrapper(const T* cppobj, bool hasOwnership = false, bool containsCppWrapper = false) +inline PyObject* SbkCreateWrapper(const T* cppobj, bool hasOwnership = false, bool isExactType = false) { return SbkBaseWrapper_New(reinterpret_cast<SbkBaseWrapperType*>(SbkType<T>()), - cppobj, hasOwnership, containsCppWrapper); + cppobj, hasOwnership, isExactType); } // Base Conversions ---------------------------------------------------------- @@ -105,7 +105,9 @@ struct ConverterBase static inline PyObject* toPython(void* cppobj) { return toPython(*reinterpret_cast<T*>(cppobj)); } static inline PyObject* toPython(const T& cppobj) { - return SbkCreateWrapper<T>(CppObjectCopier<T>::copy(cppobj), true, CppObjectCopier<T>::isCppWrapper); + PyObject* obj = SbkCreateWrapper<T>(CppObjectCopier<T>::copy(cppobj), true, true); + SbkBaseWrapper_setContainsCppWrapper(obj, CppObjectCopier<T>::isCppWrapper); + return obj; } // Classes with implicit conversions are expected to reimplement // this to build T from its various implicit constructors. diff --git a/libshiboken/typeresolver.cpp b/libshiboken/typeresolver.cpp new file mode 100644 index 000000000..3ed3daf67 --- /dev/null +++ b/libshiboken/typeresolver.cpp @@ -0,0 +1,125 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team <contact@pyside.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "typeresolver.h" +#include "google/dense_hash_map" +#include <cstdlib> + +using namespace Shiboken; + +typedef google::dense_hash_map<std::string, TypeResolver*> TypeResolverMap; +static TypeResolverMap typeResolverMap; + +struct TypeResolver::TypeResolverPrivate +{ + const char* typeName; // maybe this is not needed anymore + CppToPythonFunc cppToPython; + PythonToCppFunc pythonToCpp; + DeleteObjectFunc deleteObject; + GetPyTypeFunc getPyType; +}; + +static void deinitTypeResolver() +{ + for (TypeResolverMap::const_iterator it = typeResolverMap.begin(); it != typeResolverMap.end(); ++it) + delete it->second; + typeResolverMap.clear(); +} + +static void registerTypeResolver(TypeResolver* resolver) +{ + static bool initied = false; + if (!initied) { + assert(typeResolverMap.empty()); + typeResolverMap.set_empty_key(""); + typeResolverMap.set_deleted_key("?"); + initied = true; + std::atexit(deinitTypeResolver); + TypeResolver::createValueTypeResolver<double>("double"); + TypeResolver::createValueTypeResolver<float>("float"); + TypeResolver::createValueTypeResolver<bool>("bool"); + TypeResolver::createValueTypeResolver<int>("int"); + } + assert(typeResolverMap.find(resolver->typeName()) == typeResolverMap.end()); + typeResolverMap[resolver->typeName()] = resolver; +} + +TypeResolver::TypeResolver(const char* typeName, TypeResolver::CppToPythonFunc cppToPy, TypeResolver::PythonToCppFunc pyToCpp, GetPyTypeFunc getPyType, TypeResolver::DeleteObjectFunc deleter) +{ + m_d = new TypeResolverPrivate; + m_d->typeName = typeName; + m_d->cppToPython = cppToPy; + m_d->pythonToCpp = pyToCpp; + m_d->deleteObject = deleter; + m_d->getPyType = getPyType; + + registerTypeResolver(this); +} + +TypeResolver::~TypeResolver() +{ + delete m_d; +} + +TypeResolver* TypeResolver::get(const char* typeName) +{ + TypeResolverMap::const_iterator it = typeResolverMap.find(typeName); + return it == typeResolverMap.end() ? 0 : it->second; +} + +const char* TypeResolver::typeName() const +{ + return m_d->typeName; +} + +void* TypeResolver::toCpp(PyObject* pyObj) +{ + return m_d->pythonToCpp(pyObj); +} + +PyObject* TypeResolver::toPython(void* cppObj) +{ + return m_d->cppToPython(cppObj); +} + +void TypeResolver::deleteObject(void* object) +{ + if (m_d->deleteObject) + m_d->deleteObject(object); +} + +PyTypeObject* TypeResolver::pythonType() +{ + return m_d->getPyType(); +} diff --git a/libshiboken/typeresolver.h b/libshiboken/typeresolver.h new file mode 100644 index 000000000..4e7efb81b --- /dev/null +++ b/libshiboken/typeresolver.h @@ -0,0 +1,113 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team <contact@pyside.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef TYPERESOLVER_H +#define TYPERESOLVER_H + +#include "shibokenmacros.h" +#include "conversions.h" + +namespace Shiboken +{ + +class SbkBaseWrapperType; + +/* To C++ convertion functions. */ +template <typename T> +inline void* pythonToValueType(PyObject* pyobj) +{ + return Shiboken::CppObjectCopier<T>::copy(Shiboken::Converter<T>::toCpp(pyobj)); +} + +template <typename T> +inline void* pythonToObjectType(PyObject* pyobj) +{ + return Shiboken::Converter<T*>::toCpp(pyobj); +} + +template <typename T> +inline void objectDeleter(void* data) +{ + delete reinterpret_cast<T*>(data); +} + +template <typename T> +inline PyObject* objectTypeToPython(void* cptr) +{ + return Shiboken::Converter<T*>::toPython(*reinterpret_cast<T**>(cptr)); +} + +class LIBSHIBOKEN_API TypeResolver +{ +public: + typedef PyObject* (*CppToPythonFunc)(void*); + typedef void* (*PythonToCppFunc)(PyObject*); + typedef void (*DeleteObjectFunc)(void*); + typedef PyTypeObject* (*GetPyTypeFunc)(); + + ~TypeResolver(); + + template<typename T> + static TypeResolver* createValueTypeResolver(const char* typeName) + { + return new TypeResolver(typeName, &Shiboken::Converter<T>::toPython, &pythonToValueType<T>, &SbkType<T>, &objectDeleter<T>); + } + + template<typename T> + static TypeResolver* createObjectTypeResolver(const char* typeName) + { + return new TypeResolver(typeName, &objectTypeToPython<T>, &pythonToObjectType<T>, &SbkType<T>); + } + + static TypeResolver* get(const char* typeName); + + const char* typeName() const; + PyObject* toPython(void* cppObj); + void* toCpp(PyObject* pyObj); + void deleteObject(void* object); + PyTypeObject* pythonType(); + +private: + struct TypeResolverPrivate; + TypeResolverPrivate* m_d; + + // disable object copy + TypeResolver(const TypeResolver&); + TypeResolver& operator=(const TypeResolver&); + + TypeResolver(const char* typeName, CppToPythonFunc cppToPy, PythonToCppFunc pyToCpp, GetPyTypeFunc getPyType, DeleteObjectFunc deleter = 0); +}; +} + +#endif |