From e7afb00aea47e2082aa364bac518e523caafe499 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Sun, 19 Sep 2021 04:07:41 +1000 Subject: util/delegate.cpp: Fixed multiple issues. * Fixed this pointer displacement being reapplied after delegates are copied - caused issues with classes with multiple inheritance. * Made null member function pointer test conformant with Itanium C++ ABI specification. * Corrected size of this pointer displacement - fixes issues on big Endian targets. * Fixed function addresses for virtual member functions on targets that use function descriptors (e.g. PPC64). * Applied shift to this pointer displacement for targets that use the ARM variant of the Itanium ABI. * Fixed this pointer displacement not being applied for virtual member functions on targets using ARM variant of the Itanium ABI. * Fixed this pointer displacement being incorrectly applied to vptr on targets using ARM variant of the Itanium ABI. * Made less code conditionally compiled to make it easier to catch errors. --- src/lib/util/delegate.cpp | 80 +++++++----- src/lib/util/delegate.h | 327 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 277 insertions(+), 130 deletions(-) diff --git a/src/lib/util/delegate.cpp b/src/lib/util/delegate.cpp index 0fbc6e91f94..4b4812d3c56 100644 --- a/src/lib/util/delegate.cpp +++ b/src/lib/util/delegate.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles,Vas Crabb /*************************************************************************** delegate.cpp @@ -23,6 +23,18 @@ #define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false) #endif +// some architectures use function descriptor pointers +// usually this is a global pointer value along with the branch target +// other platforms using this convention include: +// * AIX, Classic MacOS and WinNT on 32-bit POWER/PowerPC +// * pretty much anything on Itanium +#if (defined(__ppc64__) || (defined(__PPC64__))) && !defined(__APPLE__) +#define MAME_DELEGATE_VT_DESCRIPTOR 1 +#endif +#ifndef MAME_DELEGATE_VT_DESCRIPTOR +#define MAME_DELEGATE_VT_DESCRIPTOR 0 +#endif + namespace util::detail { @@ -30,11 +42,7 @@ namespace util::detail { // GLOBAL VARIABLES //************************************************************************** -#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE) - -const delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { { 0 } }; - -#endif +const delegate_mfp_compatible::raw_mfp_data delegate_mfp_compatible::s_null_mfp = { { 0 } }; @@ -42,46 +50,56 @@ const delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { { 0 } }; // INTERNAL DELEGATE HELPERS //************************************************************************** -#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_INTERNAL) - //------------------------------------------------- // delegate_convert_raw - given an object and an raw function, adjust the object base // and return the actual final code pointer //-------------------------------------------------// -delegate_generic_function delegate_mfp::convert_to_generic(delegate_generic_class *&object) const +delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_generic_class *&object) const { -#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64) || defined(__EMSCRIPTEN__) - if ((m_this_delta & 1) == 0) +#if MAME_DELEGATE_ITANIUM_ARM + // apply the "this" delta to the object first - remember to shift right one bit position + object = reinterpret_cast(reinterpret_cast(object) + (m_this_delta >> 1)); + if (!(m_this_delta & 1)) { - LOG("Calculated Addr = %p\n", reinterpret_cast(uintptr_t(m_function))); - object = reinterpret_cast(reinterpret_cast(object) + m_this_delta); + // if the low bit of the 'this' delta is clear, the pointer is a conventional function pointer + LOG("Calculated Addr = %p\n", reinterpret_cast(m_function)); return reinterpret_cast(m_function); } - object = reinterpret_cast(reinterpret_cast(object)); - - // otherwise, it is the byte index into the vtable where the actual function lives - std::uint8_t *vtable_base = *reinterpret_cast(object); - LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast(uintptr_t(*reinterpret_cast(vtable_base + m_function + m_this_delta - 1)))); - return *reinterpret_cast(vtable_base + m_function + m_this_delta - 1); -#else + else + { + // otherwise, it is the byte index into the vtable where the actual function lives + std::uint8_t const *const vtable_ptr = *reinterpret_cast(object) + m_function; +#if MAME_DELEGATE_VT_DESCRIPTOR + delegate_generic_function const result = reinterpret_cast(uintptr_t(vtable_ptr)); +#else // MAME_DELEGATE_VT_DESCRIPTOR + delegate_generic_function const result = *reinterpret_cast(vtable_ptr); +#endif // MAME_DELEGATE_VT_DESCRIPTOR + LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast(uintptr_t(result))); + return result; + } +#else // MAME_DELEGATE_ITANIUM_ARM // apply the "this" delta to the object first object = reinterpret_cast(reinterpret_cast(object) + m_this_delta); - - // if the low bit of the vtable index is clear, then it is just a raw function pointer if (!(m_function & 1)) { - LOG("Calculated Addr = %p\n", reinterpret_cast(uintptr_t(m_function))); + // if the low bit of the pointer is clear, then it is a conventional function pointer + LOG("Calculated Addr = %p\n", reinterpret_cast(m_function)); return reinterpret_cast(m_function); } - - // otherwise, it is the byte index into the vtable where the actual function lives - std::uint8_t *vtable_base = *reinterpret_cast(object); - LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast(uintptr_t(*reinterpret_cast(vtable_base + m_function - 1)))); - return *reinterpret_cast(vtable_base + m_function - 1); -#endif + else + { + // otherwise, it is the byte index into the vtable where the actual function lives + std::uint8_t const *const vtable_ptr = *reinterpret_cast(object) + m_function - 1; +#if MAME_DELEGATE_VT_DESCRIPTOR + delegate_generic_function const result = reinterpret_cast(uintptr_t(vtable_ptr)); +#else // MAME_DELEGATE_VT_DESCRIPTOR + delegate_generic_function const result = *reinterpret_cast(vtable_ptr); +#endif // MAME_DELEGATE_VT_DESCRIPTOR + LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast(uintptr_t(result))); + return result; + } +#endif // MAME_DELEGATE_ITANIUM_ARM } -#endif - } // namespace util::detail diff --git a/src/lib/util/delegate.h b/src/lib/util/delegate.h index 2ba0c97ba2f..2d5d1afbdf7 100644 --- a/src/lib/util/delegate.h +++ b/src/lib/util/delegate.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles,Couriersud,Miodrag Milanovic +// copyright-holders:Aaron Giles,Couriersud,Miodrag Milanovic,Vas Crabb /*************************************************************************** delegate.h @@ -52,16 +52,27 @@ -------------------------------------------------------------------- - The "internal" version of delegates makes use of the internal + The "Itanium" version of delegates makes use of the internal structure of member function pointers in order to convert them at binding time into simple static function pointers. This only works on platforms where object->func(p1, p2) is equivalent in calling convention to func(object, p1, p2). - Most of the information on how this works comes from Don Clugston - in this article: + Pros: + * as fast as a standard function call in static and member cases + * no stub functions or double-hops needed - http://www.codeproject.com/KB/cpp/FastDelegate.aspx + Cons: + * requires internal knowledge of the member function pointer + * only works for two popular variants of the Itanium C++ ABI + + -------------------------------------------------------------------- + + The "MSVC" version of delegates makes use of the internal structure + of member function pointers in order to convert them at binding time + into simple static function pointers. This only works on platforms + where object->func(p1, p2) is equivalent in calling convention to + func(object, p1, p2). Pros: * as fast as a standard function call in static and member cases @@ -69,7 +80,17 @@ Cons: * requires internal knowledge of the member function pointer - * only works for GCC (for now; MSVC info is also readily available) + * only works works with MSVC ABI, and not on 32-bit x86 + * does not work for classes with virtual inheritance + * structure return does not work with member function pointers + + -------------------------------------------------------------------- + + Further reading: + + http://www.codeproject.com/KB/cpp/FastDelegate.aspx + + http://itanium-cxx-abi.github.io/cxx-abi/abi.html#member-pointers ***************************************************************************/ #ifndef MAME_UTIL_DELEGATE_H @@ -93,37 +114,47 @@ //************************************************************************** // types of delegates supported -#define DELEGATE_TYPE_COMPATIBLE 0 -#define DELEGATE_TYPE_INTERNAL 1 -#define DELEGATE_TYPE_MSVC 2 +#define MAME_DELEGATE_TYPE_COMPATIBLE 0 +#define MAME_DELEGATE_TYPE_ITANIUM 1 +#define MAME_DELEGATE_TYPE_MSVC 2 // select which one we will be using -#if defined(FORCE_COMPATIBLE) - #define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE +#if defined(MAME_DELEGATE_FORCE_COMPATIBLE) + #define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE #elif defined(__GNUC__) - /* 32bit MINGW asks for different convention */ + // 32bit MINGW asks for different convention #if defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__) - #define USE_DELEGATE_TYPE DELEGATE_TYPE_INTERNAL - #define MEMBER_ABI __thiscall - #define HAS_DIFFERENT_ABI 1 + #define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_ITANIUM + #define MAME_DELEGATE_MEMBER_ABI __thiscall + #define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 1 #elif defined(__clang__) && defined(__i386__) && defined(_WIN32) - #define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE + #define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE #else - #define USE_DELEGATE_TYPE DELEGATE_TYPE_INTERNAL - #define MEMBER_ABI - #define HAS_DIFFERENT_ABI 0 + #define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_ITANIUM + #define MAME_DELEGATE_MEMBER_ABI + #define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0 #endif #elif defined(_MSC_VER) && defined(_M_X64) - #define MEMBER_ABI - #define HAS_DIFFERENT_ABI 0 - #define USE_DELEGATE_TYPE DELEGATE_TYPE_MSVC + #define MAME_DELEGATE_MEMBER_ABI + #define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0 + #define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_MSVC +#else + #define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE +#endif + +#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) + #define MAME_DELEGATE_ITANIUM_ARM 1 +#elif defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64) + #define MAME_DELEGATE_ITANIUM_ARM 1 +#elif defined(__EMSCRIPTEN__) + #define MAME_DELEGATE_ITANIUM_ARM 1 #else - #define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE + #define MAME_DELEGATE_ITANIUM_ARM 0 #endif -#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE) - #define MEMBER_ABI - #define HAS_DIFFERENT_ABI 0 +#if USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE + #define MAME_DELEGATE_MEMBER_ABI + #define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0 #endif @@ -164,50 +195,49 @@ struct delegate_traits -//************************************************************************** -// DELEGATE MEMBER FUNCTION POINTER WRAPPERS -//************************************************************************** - -#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE) - -// ======================> delegate_mfp - -// delegate_mfp is a class that wraps a generic member function pointer -// in a static buffer, and can effectively recast itself back for later use; -// it hides some of the gross details involved in copying arbitrary member -// function pointers around -class delegate_mfp +/// \brief Maximally compatible member function pointer wrapper +/// +/// Instantiates a static member function template on construction as +/// an adaptor thunk to call the supplied member function with the +/// supplied object. Adds one layer of indirection to calls. +/// +/// This implementation requires the representation of a null member +/// function pointer to be all zeroes. +class delegate_mfp_compatible { public: // default constructor - delegate_mfp() + delegate_mfp_compatible() : m_rawdata(s_null_mfp) , m_realobject(nullptr) , m_stubfunction(nullptr) { } // copy constructor - delegate_mfp(const delegate_mfp &src) = default; + delegate_mfp_compatible(const delegate_mfp_compatible &src) = default; // construct from any member function pointer template - delegate_mfp(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType) + delegate_mfp_compatible(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType) : m_rawdata(s_null_mfp) , m_realobject(nullptr) - , m_stubfunction(make_generic(&delegate_mfp::method_stub)) + , m_stubfunction(make_generic(&delegate_mfp_compatible::method_stub)) { static_assert(sizeof(mfp) <= sizeof(m_rawdata), "Unsupported member function pointer size"); *reinterpret_cast(&m_rawdata) = mfp; } // comparison helpers - bool operator==(const delegate_mfp &rhs) const { return (m_rawdata == rhs.m_rawdata); } - bool isnull() const { return (m_rawdata == s_null_mfp); } + bool operator==(const delegate_mfp_compatible &rhs) const { return m_rawdata == rhs.m_rawdata; } + bool isnull() const { return m_rawdata == s_null_mfp; } // getters - delegate_generic_class *real_object(delegate_generic_class *original) const { return m_realobject; } + delegate_generic_class *real_object(delegate_generic_class *original) const + { + return m_realobject; + } - // binding helper + // binding helpers template void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object) { @@ -216,18 +246,24 @@ public: funcptr = reinterpret_cast(m_stubfunction); } + template + void update_after_copy(FunctionType &funcptr, delegate_generic_class *&object) + { + assert(reinterpret_cast(m_stubfunction) == funcptr); + object = reinterpret_cast(this); + } + private: // helper stubs for calling encased member function pointers template static ReturnType method_stub(delegate_generic_class *object, Params ... args) { - delegate_mfp *_this = reinterpret_cast(object); using mfptype = ReturnType(FunctionClass::*)(Params...); - mfptype &mfp = *reinterpret_cast(&_this->m_rawdata); - return (reinterpret_cast(_this->m_realobject)->*mfp)(std::forward(args)...); + delegate_mfp_compatible &_this = *reinterpret_cast(object); + mfptype &mfp = *reinterpret_cast(&_this.m_rawdata); + return (reinterpret_cast(_this.m_realobject)->*mfp)(std::forward(args)...); } - // helper to convert a function of a given type to a generic function, forcing template // instantiation to match the source type template @@ -236,7 +272,7 @@ private: return reinterpret_cast(funcptr); } - + // FIXME: not properly aligned for storing pointers struct raw_mfp_data { #if defined(__INTEL_COMPILER) && defined(_M_X64) // needed for "Intel(R) C++ Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 14.0.2.176 Build 20140130" at least @@ -244,7 +280,7 @@ private: #else // all other cases - for MSVC maximum size is one pointer, plus 3 ints; all other implementations seem to be smaller int data[((sizeof(void *) + 3 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)]; #endif - bool operator==(const raw_mfp_data &rhs) const { return (memcmp(data, rhs.data, sizeof(data)) == 0); } + bool operator==(const raw_mfp_data &rhs) const { return !std::memcmp(data, rhs.data, sizeof(data)); } }; // internal state @@ -255,106 +291,195 @@ private: static const raw_mfp_data s_null_mfp; // nullptr mfp }; -#elif (USE_DELEGATE_TYPE == DELEGATE_TYPE_INTERNAL) -// ======================> delegate_mfp -// struct describing the contents of a member function pointer -class delegate_mfp +/// \brief Itanium C++ ABI member function pointer wrapper +/// +/// Supports the two most popular pointer to member function +/// implementations described in the Itanium C++ ABI. Both of these +/// consist of a pointer followed by a pointer followed by a ptrdiff_t. +/// +/// The first variant is used when member the least significant bit of a +/// member function pointer need never be set and vtable entry offsets +/// are guaranteed to be even numbers of bytes. If the pointer is even, +/// it is a conventional function pointer to the member function. If +/// the pointer is odd, it is a byte offset into the vtable plus one. +/// The ptrdiff_t is a byte offset to add to the this pointer. A null +/// member function pointer is represented by setting the pointer to +/// a null pointer. +/// +/// The second variant is used when the least significant bit of a +/// pointer to a member function may need to be set or it may not be +/// possible to distinguish between a vtable offset and a null pointer. +/// (This is the case for ARM where the least significant bit of a +/// pointer to a function is set if the function starts in Thumb mode.) +/// If the least significant bit of the ptrdiff_t is clear, the pointer +/// is a conventional function pointer to the member function. If the +/// least significant bit of the ptrdiff_t is set, the pointer is a byte +/// offset into the vtable. The ptrdiff_t must be shifted right one bit +/// position to make a byte offset to add to the this pointer. A null +/// member function pointer is represented by setting the pointer to a +/// null pointer and clearing the least significant bit of the +/// ptrdiff_t. +class delegate_mfp_itanium { public: // default constructor - delegate_mfp() = default; + delegate_mfp_itanium() = default; // copy constructor - delegate_mfp(const delegate_mfp &src) = default; + delegate_mfp_itanium(const delegate_mfp_itanium &src) = default; // construct from any member function pointer template - delegate_mfp(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType) + delegate_mfp_itanium(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType) { static_assert(sizeof(mfp) == sizeof(*this), "Unsupported member function pointer size"); *reinterpret_cast(this) = mfp; } // comparison helpers - bool operator==(const delegate_mfp &rhs) const { return (m_function == rhs.m_function) && (m_this_delta == rhs.m_this_delta); } - bool isnull() const { return !m_function && !m_this_delta; } + bool operator==(const delegate_mfp_itanium &rhs) const + { + return (m_function == rhs.m_function) && (m_this_delta == rhs.m_this_delta); + } + bool isnull() const + { +#if MAME_DELEGATE_ITANIUM_ARM + return !reinterpret_cast(m_function) && !(m_this_delta & 1); +#else + return !reinterpret_cast(m_function); +#endif + } // getters - static delegate_generic_class *real_object(delegate_generic_class *original) { return original; } + static delegate_generic_class *real_object(delegate_generic_class *original) + { + return original; + } - // binding helper + // binding helpers template void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object) { funcptr = reinterpret_cast(convert_to_generic(object)); } + template + void update_after_copy(FunctionType &funcptr, delegate_generic_class *&object) + { + } + private: // extract the generic function and adjust the object pointer delegate_generic_function convert_to_generic(delegate_generic_class *&object) const; // actual state - uintptr_t m_function = 0; // first item can be one of two things: - // if even, it's a pointer to the function - // if odd, it's the byte offset into the vtable - int m_this_delta = 0; // delta to apply to the 'this' pointer + uintptr_t m_function = reinterpret_cast(static_cast(nullptr)); // function pointer or vtable offset + ptrdiff_t m_this_delta = 0; // delta to apply to the 'this' pointer }; -#elif (USE_DELEGATE_TYPE == DELEGATE_TYPE_MSVC) -// ======================> delegate_mfp -// struct describing the contents of a member function pointer -class delegate_mfp +/// \brief MSVC member function pointer wrapper +/// +/// MSVC uses space optimisation. A member function pointer is a +/// conventional function pointer followed by zero to three int values, +/// depending on whether the class has single, multiple, virtual or +/// unknown inheritance of base classes. The function pointer is always +/// a conventional function pointer (a thunk is used to call virtual +/// member functions through the vtable). +/// +/// If present, the first int value is a byte offset to add to the this +/// pointer before calling the function. +/// +/// For the virtual inheritance case, the offset to the vtable pointer +/// from the location the this pointer points to must be known by the +/// compiler when the member function pointer is called. The second int +/// value is a byte offset into the vtable to an int value containing an +/// additional byte offset to add to the this pointer. +/// +/// For the unknown inheritance case, the second int value is a byte +/// offset add to the this pointer to obtain a pointer to the vtable +/// pointer, or undefined if not required. If the third int value is +/// not zero, it is a byte offset into the vtable to an int value +/// containing an additional byte offset to add to the this pointer. +/// +/// It is not possible to support the virtual inheritance case without +/// some way of obtaining the offset to the vtable pointer. +class delegate_mfp_msvc { - struct single_base_equiv { delegate_generic_function p; }; - struct multi_base_equiv { delegate_generic_function p; int o; }; + struct single_base_equiv { delegate_generic_function fptr; }; + struct multi_base_equiv { delegate_generic_function fptr; int thisdisp; }; + struct unknown_base_equiv { delegate_generic_function fptr; int thisdisp, vptrdisp, vtdisp; }; public: // default constructor - delegate_mfp() = default; + delegate_mfp_msvc() = default; // copy constructor - delegate_mfp(const delegate_mfp &src) = default; + delegate_mfp_msvc(const delegate_mfp_msvc &src) = default; // construct from any member function pointer template - delegate_mfp(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType) + delegate_mfp_msvc(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType) { - static_assert((sizeof(mfp) == sizeof(single_base_equiv)) || (sizeof(mfp) == sizeof(multi_base_equiv)), "Unsupported member function pointer size"); - m_size = sizeof(mfp); + // FIXME: this doesn't actually catch the unsupported virtual inheritance case on 64-bit targets + // alignment of the pointer means sizeof gives the same value for multiple inheritance and virtual inheritance cases + static_assert( + (sizeof(mfp) == sizeof(single_base_equiv)) || (sizeof(mfp) == sizeof(multi_base_equiv)) || (sizeof(mfp) == sizeof(unknown_base_equiv)), + "Unsupported member function pointer size"); + static_assert(sizeof(mfp) <= sizeof(*this), "Member function pointer is too large to support"); *reinterpret_cast(this) = mfp; + m_size = sizeof(mfp); } // comparison helpers - bool operator==(const delegate_mfp &rhs) const { return m_function == rhs.m_function; } - bool isnull() const { return !m_function; } + bool operator==(const delegate_mfp_msvc &rhs) const { return m_function == rhs.m_function; } + bool isnull() const { return !reinterpret_cast(m_function); } // getters static delegate_generic_class *real_object(delegate_generic_class *original) { return original; } - // binding helper + // binding helpers template void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object) { funcptr = reinterpret_cast(m_function); - if (m_size > sizeof(single_base_equiv)) - object = reinterpret_cast(reinterpret_cast(object) + m_this_delta); + std::uint8_t *byteptr = reinterpret_cast(object); + if ((sizeof(unknown_base_equiv) == m_size) && m_vt_index) + { + std::uint8_t const *const vptr = *reinterpret_cast(byteptr + m_vptr_offs); + byteptr += *reinterpret_cast(vptr + m_vt_index); + } + if (sizeof(single_base_equiv) < m_size) + byteptr += m_this_delta; + object = reinterpret_cast(byteptr); + } + + template + void update_after_copy(FunctionType &funcptr, delegate_generic_class *&object) + { } private: // actual state - uintptr_t m_function = 0; // pointer to function or non-virtual thunk for vtable index + uintptr_t m_function = 0; // pointer to function or non-virtual thunk for virtual function call int m_this_delta = 0; // delta to apply to the 'this' pointer for multiple inheritance - - int m_dummy1 = 0; - int m_dummy2 = 0; + int m_vptr_offs = 0; // offset to apply to this pointer to obtain pointer to vptr + int m_vt_index = 0; // offset into vtable to additional delta to apply to the 'this' pointer unsigned m_size = 0; }; + + +#if USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE +using delegate_mfp = delegate_mfp_compatible; +#elif USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_ITANIUM +using delegate_mfp = delegate_mfp_itanium; +#elif USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_MSVC +using delegate_mfp = delegate_mfp_msvc; #endif } // namespace util::detail @@ -400,7 +525,7 @@ public: // define our traits template using traits = util::detail::delegate_traits; using generic_static_func = typename traits::static_func_type; - typedef MEMBER_ABI generic_static_func generic_member_func; + typedef MAME_DELEGATE_MEMBER_ABI generic_static_func generic_member_func; // generic constructor delegate_base() = default; @@ -408,11 +533,13 @@ public: // copy constructor delegate_base(const delegate_base &src) : m_function(src.m_function) + , m_object(src.m_object) , m_latebinder(src.m_latebinder) , m_raw_function(src.m_raw_function) , m_raw_mfp(src.m_raw_mfp) { - bind(src.object()); + if (src.object() && is_mfp()) + m_raw_mfp.update_after_copy(m_function, m_object); } // copy constructor with late bind @@ -431,7 +558,7 @@ public: : m_latebinder(&late_bind_helper) , m_raw_mfp(funcptr, object, static_cast(nullptr), static_cast(nullptr)) { - bind(reinterpret_cast(object)); + bind(object); } template @@ -439,7 +566,7 @@ public: : m_latebinder(&late_bind_helper) , m_raw_mfp(funcptr, object, static_cast(nullptr), static_cast(nullptr)) { - bind(reinterpret_cast(object)); + bind(object); } // construct from static reference function with object reference @@ -449,7 +576,7 @@ public: , m_latebinder(&late_bind_helper) , m_raw_function(reinterpret_cast(funcptr)) { - bind(reinterpret_cast(object)); + bind(object); } // copy operator @@ -458,12 +585,13 @@ public: if (this != &src) { m_function = src.m_function; - m_object = nullptr; + m_object = src.m_object; m_latebinder = src.m_latebinder; m_raw_function = src.m_raw_function; m_raw_mfp = src.m_raw_mfp; - bind(src.object()); + if (src.object() && is_mfp()) + m_raw_mfp.update_after_copy(m_function, m_object); } return *this; } @@ -478,7 +606,7 @@ public: // call the function ReturnType operator()(Params... args) const { - if (is_mfp() && (HAS_DIFFERENT_ABI)) + if ((MAME_DELEGATE_DIFFERENT_MEMBER_ABI) && is_mfp()) return (*reinterpret_cast(m_function))(m_object, std::forward(args)...); else return (*m_function)(m_object, std::forward(args)...); @@ -517,9 +645,10 @@ protected: } // bind the actual object - void bind(util::detail::delegate_generic_class *object) + template + void bind(FunctionClass *object) { - m_object = object; + m_object = reinterpret_cast(object); // if we're wrapping a member function pointer, handle special stuff if (m_object && is_mfp()) @@ -615,7 +744,7 @@ protected: template using const_member_func_type = typename traits::const_member_func_type; template using static_ref_func_type = typename traits::static_ref_func_type; - template using suitable_functoid = std::bool_constant, ReturnType> || std::is_same_v >, ReturnType> >; + template using suitable_functoid = std::is_invocable_r; public: delegate() : basetype() { } -- cgit v1.2.3