diff --git a/.appveyor.yml b/.appveyor.yml index 70fbba2..7d15130 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -16,6 +16,10 @@ environment: - APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu MICROPY_CPYTHON3: python3 FullTypeCheck: 1 + - APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu + MICROPY_CPYTHON3: python3 + FullTypeCheck: 1 + ExtraCppFlags: -fno-rtti configuration: - Debug @@ -112,7 +116,7 @@ for: build_script: - ps: | - make test CC=gcc-9 CXX=g++-9 CPPFLAGS_EXTRA="-DUPYWRAP_FULLTYPECHECK=$env:FullTypeCheck" + make test CC=gcc-9 CXX=g++-9 CPPFLAGS_EXTRA="-DUPYWRAP_FULLTYPECHECK=$env:FullTypeCheck $env:ExtraCppFlags" on_failure: - ps: | diff --git a/classwrapper.h b/classwrapper.h index d341c7f..c7f6e47 100644 --- a/classwrapper.h +++ b/classwrapper.h @@ -316,7 +316,7 @@ namespace upywrap o->base.type = (const mp_obj_type_t*) & type; o->cookie = defCookie; #if UPYWRAP_FULLTYPECHECK - o->typeId = &typeid( T ); + o->typeId = ClassHash< T >(); #endif #if UPYWRAP_SHAREDPTROBJ new( &o->obj ) native_obj_t( std::move( p ) ); @@ -354,7 +354,7 @@ namespace upywrap //preferrably using dynamic_cast or dynamic_pointer_cast to double-check errors. if( !mp_obj_is_obj( arg ) || native->cookie != defCookie #if UPYWRAP_FULLTYPECHECK - || typeid( T ) != *native->typeId + || ClassHash< T >() != native->typeId #endif ) { @@ -919,7 +919,7 @@ namespace upywrap mp_obj_base_t base; //must always be the first member! std::int64_t cookie; //we'll use this to check if a pointer really points to a ClassWrapper - const std::type_info* typeId; //and this will be used to check if types aren't being mixed + const void* typeId; //and this will be used to check if types aren't being mixed native_obj_t obj; static mp_obj_full_type_t type; static function_ptrs functionPointers; @@ -1113,7 +1113,11 @@ namespace upywrap { //Note: registered once, stays forever. Alternative would be to have a finalizer //but then when a new function is needed it again has to go through initialization. +#if UPYWRAP_HAS_TYPEID static wrapper_t reg( typeid( funct_t ).name(), wrapper_t::ConstructorOptions::RegisterInStaticPyObjectStore ); +#else + static wrapper_t reg( "AnonymousClasswrapper", wrapper_t::ConstructorOptions::RegisterInStaticPyObjectStore ); +#endif static bool init = false; if( !init ) { diff --git a/detail/configuration.h b/detail/configuration.h index fc88f85..9776fa5 100644 --- a/detail/configuration.h +++ b/detail/configuration.h @@ -103,22 +103,11 @@ //Require exact type matches when converting uPy objects into native ones. //By default this is on in order to get proper error messages when passing mismatching types, //instead of possibly invoking undefined behavior by casting unrelated types. -//However when your application wants to passs pointers to derived classes to functions +//However when your application wants to pass pointers to derived classes to functions //taking base class pointers this has to be turned off. -//Note: this requires C++ type information. For gcc (and possibly others) this means that it -//doesn't work with -fno-rtti because that makes typeid() unavailable; which striclty speaking -//shouldn't be because typeid() could work fine on types known at compile-time, as we use it. -//Following that reasoning as well, msvc has no problems with it. -//Not turned off automatically in case of !UPYWRAP_HAS_TYPEID because we want users to -//be explicit about disabling this option. #ifndef UPYWRAP_FULLTYPECHECK #define UPYWRAP_FULLTYPECHECK (1) #endif -#if defined( __cplusplus ) && !defined(NO_QSTR) -#if UPYWRAP_FULLTYPECHECK && !UPYWRAP_HAS_TYPEID -#error "UPYWRAP_FULLTYPECHECK requires RTTI. Build with -frtti or -DUPYWRAP_FULLTYPECHECK=0 (not advisable)" -#endif -#endif //Maximum number of keyword arguments a wrapped function call can support. //When parsing the arguments are stack-allocated for speed, and instead of fiddling with alloca() diff --git a/detail/util.h b/detail/util.h index 9e4b945..b19c1ad 100644 --- a/detail/util.h +++ b/detail/util.h @@ -125,6 +125,25 @@ namespace upywrap struct is_shared_ptr< std::shared_ptr< T > > : std::true_type { }; + + /** + * @brief Replacement for typeid without RTTI. This uniquely identifies the classwrapper type within an executable. + * @warning The hashes will change when recompiling or on other targets. Do not use cross-device or cross-executable. + * + * The functionality is based around the One Definition Rule (ODR), there must be exactly one definition of this function + * per class, even across translation units. + */ + template< class T > + inline void* ClassHashImpl() { + static int static_hash_marker = 0; // The address of this object is the unique hash of this classwrapper. + return &static_hash_marker; + } + + template< class T > + inline void* ClassHash() { + // This assumes we do not make differences between differently qualified types. + return ClassHashImpl::type>::type>(); + } } #endif //#ifndef MICROPYTHON_WRAP_DETAIL_UTIL_H