Skip to content

Introduce ClassHash to uniquely identify classwrappers, make -fno-rtti work #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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: |
Expand Down
10 changes: 7 additions & 3 deletions classwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) );
Expand Down Expand Up @@ -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
)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 )
{
Expand Down
13 changes: 1 addition & 12 deletions detail/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
19 changes: 19 additions & 0 deletions detail/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<typename std::remove_cv<typename std::remove_reference<T>::type>::type>();
}
}

#endif //#ifndef MICROPYTHON_WRAP_DETAIL_UTIL_H