mirror of
https://github.com/holub/mame
synced 2025-06-17 17:59:05 +03:00
util/delegate.h: Try to catch issues earlier, and some cleanup.
Optimised generation of late bind helper functions. The late bind helper function doesn't depend on the delegate signature - only on the late bind base class and function target class. Having it inside the delegate base class means it needs to be instantiated for every combination of late bind base class, function target class and delegate signature. In a typical driver file, there is only one late bind base class (delegate_late_bind), and there will be delegates with multiple signatures bound to function members of the same class (e.g. read and write handlers, possibly of different widths, bound to members of the driver state class). By moving the late bind helper out of the delegate base class, the number of required instantiations can be reduced. By moving the body out of the enclosing class declaration, the compiler can be encouraged to coalese instantiations across translation units. While this won't give a further reduction in compile time, it should at least reduce the output binary size by reducing duplication for devices that frequently have handlers installed in memory maps. Added an additional template parameter to delegates allowing the late bind base class to be changed if desired. Moved the MSVC implementation "this" pointer optimisation out-of-line and added logging. Also cleaned up the Itanium "this" pointer adjustment and code pointer resolution implementation to reduce duplication and conditional compilation. Made binding_type_exception give a more meaningful what() message than "std::exception". Added extensive validity tests for delegate functionality. Pointers to member functions are tested, including multiple inheritance, virtual and non-virtual member functions, and checking for generational loss across copying/assigning delegates. This should properly exercise "this" pointer adjustment for the Itanium and MSVC implementations, and vtable lookup for the Itanium implementation. So-called late binding functionality is tested, including exceptions on failure. Functoids are tested, although given the encapsulation it's not possible to check that an apator isn't generated when it shouldn't be.
This commit is contained in:
parent
566a741f4f
commit
401b22bbf5
@ -414,7 +414,7 @@ int running_machine::run(bool quiet)
|
|||||||
}
|
}
|
||||||
catch (binding_type_exception &btex)
|
catch (binding_type_exception &btex)
|
||||||
{
|
{
|
||||||
osd_printf_error("Error performing a late bind of type %s to %s\n", btex.m_actual_type.name(), btex.m_target_type.name());
|
osd_printf_error("Error performing a late bind of function expecting type %s to instance of type %s\n", btex.target_type().name(), btex.actual_type().name());
|
||||||
error = EMU_ERR_FATALERROR;
|
error = EMU_ERR_FATALERROR;
|
||||||
}
|
}
|
||||||
catch (tag_add_exception &aex)
|
catch (tag_add_exception &aex)
|
||||||
|
1119
src/emu/validity.cpp
1119
src/emu/validity.cpp
File diff suppressed because it is too large
Load Diff
@ -60,7 +60,6 @@ private:
|
|||||||
using string_set = std::unordered_set<std::string>;
|
using string_set = std::unordered_set<std::string>;
|
||||||
|
|
||||||
// internal helpers
|
// internal helpers
|
||||||
const char *ioport_string_from_index(u32 index);
|
|
||||||
int get_defstr_index(const char *string, bool suppress_error = false);
|
int get_defstr_index(const char *string, bool suppress_error = false);
|
||||||
|
|
||||||
// core helpers
|
// core helpers
|
||||||
@ -69,9 +68,6 @@ private:
|
|||||||
void validate_one(const game_driver &driver);
|
void validate_one(const game_driver &driver);
|
||||||
|
|
||||||
// internal sub-checks
|
// internal sub-checks
|
||||||
void validate_core();
|
|
||||||
void validate_inlines();
|
|
||||||
void validate_rgb();
|
|
||||||
void validate_driver(device_t &root);
|
void validate_driver(device_t &root);
|
||||||
void validate_roms(device_t &root);
|
void validate_roms(device_t &root);
|
||||||
void validate_analog_input_field(const ioport_field &field);
|
void validate_analog_input_field(const ioport_field &field);
|
||||||
@ -86,12 +82,6 @@ private:
|
|||||||
template <typename Format, typename... Params> void output_via_delegate(osd_output_channel channel, Format &&fmt, Params &&...args);
|
template <typename Format, typename... Params> void output_via_delegate(osd_output_channel channel, Format &&fmt, Params &&...args);
|
||||||
void output_indented_errors(std::string &text, const char *header);
|
void output_indented_errors(std::string &text, const char *header);
|
||||||
|
|
||||||
// random number generation
|
|
||||||
s32 random_i32();
|
|
||||||
u32 random_u32();
|
|
||||||
s64 random_i64();
|
|
||||||
u64 random_u64();
|
|
||||||
|
|
||||||
// internal driver list
|
// internal driver list
|
||||||
driver_enumerator m_drivlist;
|
driver_enumerator m_drivlist;
|
||||||
|
|
||||||
|
@ -11,19 +11,20 @@
|
|||||||
#include "delegate.h"
|
#include "delegate.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
|
||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
// MACROS
|
// MACROS
|
||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
|
|
||||||
#if defined(LOG_DELEGATES)
|
#if defined(MAME_DELEGATE_LOG_ADJ)
|
||||||
#define LOG(...) printf(__VA_ARGS__)
|
#define LOG(...) printf(__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
#define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false)
|
#define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// some architectures use function descriptor pointers
|
// on some architectures, function pointers point to descriptors
|
||||||
// usually this is a global pointer value along with the branch target
|
// usually this is a global pointer value along with the branch target
|
||||||
// other platforms using this convention include:
|
// other platforms using this convention include:
|
||||||
// * AIX, Classic MacOS and WinNT on 32-bit POWER/PowerPC
|
// * AIX, Classic MacOS and WinNT on 32-bit POWER/PowerPC
|
||||||
@ -36,6 +37,28 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//**************************************************************************
|
||||||
|
// LATE BINDING EXCEPTION
|
||||||
|
//**************************************************************************
|
||||||
|
|
||||||
|
binding_type_exception::binding_type_exception(std::type_info const &target_type, std::type_info const &actual_type)
|
||||||
|
: m_target_type(&target_type)
|
||||||
|
, m_actual_type(&actual_type)
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "Error performing late bind of function expecting type " << target_type.name() << " to instance of type " << actual_type.name();
|
||||||
|
m_what = os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char const *binding_type_exception::what() const noexcept
|
||||||
|
{
|
||||||
|
return m_what.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace util::detail {
|
namespace util::detail {
|
||||||
|
|
||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
@ -51,55 +74,65 @@ const delegate_mfp_compatible::raw_mfp_data delegate_mfp_compatible::s_null_mfp
|
|||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
// delegate_convert_raw - given an object and an raw function, adjust the object base
|
// delegate_mfp_itanium::convert_to_generic -
|
||||||
// and return the actual final code pointer
|
// given an object pointer and member function
|
||||||
//-------------------------------------------------//
|
// pointer, apply the displacement and get the
|
||||||
|
// actual function pointer
|
||||||
|
//-------------------------------------------------
|
||||||
|
|
||||||
delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_generic_class *&object) const
|
delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_generic_class *&object) const
|
||||||
{
|
{
|
||||||
#if MAME_DELEGATE_ITANIUM_ARM
|
// apply the "this" delta to the object first - the value is shifted to the left one bit position for the ARM-like variant
|
||||||
// apply the "this" delta to the object first - remember to shift right one bit position
|
LOG("Input this=%p ptr=%p adj=%ld ", reinterpret_cast<void const *>(object), reinterpret_cast<void const *>(m_function), long(m_this_delta));
|
||||||
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> 1));
|
object = reinterpret_cast<delegate_generic_class *>(
|
||||||
if (!(m_this_delta & 1))
|
reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> (MAME_DELEGATE_ITANIUM_ARM ? 1 : 0)));
|
||||||
|
LOG("Calculated this=%p ", reinterpret_cast<void const *>(object));
|
||||||
|
|
||||||
|
// test the virtual member function flag - it's the low bit of either the ptr or adj field, depending on the variant
|
||||||
|
if (MAME_DELEGATE_ITANIUM_ARM ? !(m_this_delta & 1) : !(m_function & 1))
|
||||||
{
|
{
|
||||||
// if the low bit of the 'this' delta is clear, the pointer is a conventional function pointer
|
// conventional function pointer
|
||||||
LOG("Calculated Addr = %p\n", reinterpret_cast<void const *>(m_function));
|
LOG("ptr=%p\n", reinterpret_cast<void const *>(m_function));
|
||||||
return reinterpret_cast<delegate_generic_function>(m_function);
|
return reinterpret_cast<delegate_generic_function>(m_function);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// otherwise, it is the byte index into the vtable where the actual function lives
|
// byte index into the vtable to the function
|
||||||
std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function;
|
std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function - (MAME_DELEGATE_ITANIUM_ARM ? 0 : 1);
|
||||||
#if MAME_DELEGATE_VT_DESCRIPTOR
|
delegate_generic_function result;
|
||||||
delegate_generic_function const result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr));
|
if (MAME_DELEGATE_VT_DESCRIPTOR)
|
||||||
#else // MAME_DELEGATE_VT_DESCRIPTOR
|
result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr));
|
||||||
delegate_generic_function const result = *reinterpret_cast<delegate_generic_function const *>(vtable_ptr);
|
|
||||||
#endif // MAME_DELEGATE_VT_DESCRIPTOR
|
|
||||||
LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast<void const *>(uintptr_t(result)));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#else // MAME_DELEGATE_ITANIUM_ARM
|
|
||||||
// apply the "this" delta to the object first
|
|
||||||
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
|
|
||||||
if (!(m_function & 1))
|
|
||||||
{
|
|
||||||
// if the low bit of the pointer is clear, then it is a conventional function pointer
|
|
||||||
LOG("Calculated Addr = %p\n", reinterpret_cast<void const *>(m_function));
|
|
||||||
return reinterpret_cast<delegate_generic_function>(m_function);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
result = *reinterpret_cast<delegate_generic_function const *>(vtable_ptr);
|
||||||
// otherwise, it is the byte index into the vtable where the actual function lives
|
LOG("ptr=%p (vtable)\n", reinterpret_cast<void const *>(result));
|
||||||
std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function - 1;
|
|
||||||
#if MAME_DELEGATE_VT_DESCRIPTOR
|
|
||||||
delegate_generic_function const result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr));
|
|
||||||
#else // MAME_DELEGATE_VT_DESCRIPTOR
|
|
||||||
delegate_generic_function const result = *reinterpret_cast<delegate_generic_function const *>(vtable_ptr);
|
|
||||||
#endif // MAME_DELEGATE_VT_DESCRIPTOR
|
|
||||||
LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast<void const *>(uintptr_t(result)));
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif // MAME_DELEGATE_ITANIUM_ARM
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------------------------
|
||||||
|
// delegate_mfp_msvc::adjust_this_pointer - given
|
||||||
|
// an object pointer and member function pointer,
|
||||||
|
// apply the displacement
|
||||||
|
//-------------------------------------------------
|
||||||
|
|
||||||
|
void delegate_mfp_msvc::adjust_this_pointer(delegate_generic_class *&object) const
|
||||||
|
{
|
||||||
|
LOG("Input this=%p ", reinterpret_cast<void const *>(object));
|
||||||
|
if (sizeof(single_base_equiv) < m_size)
|
||||||
|
LOG("thisdelta=%d ", m_this_delta);
|
||||||
|
if (sizeof(unknown_base_equiv) == m_size)
|
||||||
|
LOG("vptrdelta=%d vindex=%d ", m_vptr_offs, m_vt_index);
|
||||||
|
std::uint8_t *byteptr = reinterpret_cast<std::uint8_t *>(object);
|
||||||
|
if ((sizeof(unknown_base_equiv) == m_size) && m_vt_index)
|
||||||
|
{
|
||||||
|
std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(byteptr + m_vptr_offs);
|
||||||
|
byteptr += *reinterpret_cast<int const *>(vptr + m_vt_index);
|
||||||
|
}
|
||||||
|
if (sizeof(single_base_equiv) < m_size)
|
||||||
|
byteptr += m_this_delta;
|
||||||
|
LOG("Calculated this=%p\n", reinterpret_cast<void const *>(byteptr));
|
||||||
|
object = reinterpret_cast<delegate_generic_class *>(byteptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace util::detail
|
} // namespace util::detail
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
The "compatible" version of delegates is based on an implementation
|
The "compatible" version of delegates is based on an implementation
|
||||||
from Sergey Ryazanov, found here:
|
from Sergey Ryazanov, found here:
|
||||||
|
|
||||||
http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx
|
https://www.codeproject.com/Articles/11015/The-Impossibly-Fast-C-Delegates
|
||||||
|
|
||||||
These delegates essentially generate a templated static stub function
|
These delegates essentially generate a templated static stub function
|
||||||
for each target function. The static function takes the first
|
for each target function. The static function takes the first
|
||||||
@ -75,26 +75,42 @@
|
|||||||
func(object, p1, p2).
|
func(object, p1, p2).
|
||||||
|
|
||||||
Pros:
|
Pros:
|
||||||
* as fast as a standard function call in static and member cases
|
* as fast as a standard function call in static and non-virtual
|
||||||
* no stub functions or double-hops needed
|
member cases
|
||||||
|
* no stub functions needed
|
||||||
|
|
||||||
Cons:
|
Cons:
|
||||||
* requires internal knowledge of the member function pointer
|
* requires internal knowledge of the member function pointer
|
||||||
* only works works with MSVC ABI, and not on 32-bit x86
|
* only works works with MSVC ABI, and not on 32-bit x86
|
||||||
* does not work for classes with virtual inheritance
|
* does not work for classes with virtual bases
|
||||||
* structure return does not work with member function pointers
|
* structure return does not work with member function pointers
|
||||||
|
* virtual member function lookup cannot be done in advance
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
Further reading:
|
Further reading:
|
||||||
|
|
||||||
http://www.codeproject.com/KB/cpp/FastDelegate.aspx
|
* http://itanium-cxx-abi.github.io/cxx-abi/abi.html#member-pointers
|
||||||
|
Formal specification for the most common member function pointer
|
||||||
|
implementations.
|
||||||
|
|
||||||
http://itanium-cxx-abi.github.io/cxx-abi/abi.html#member-pointers
|
* https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
|
||||||
|
Discusses many member function pointer implementations. Based
|
||||||
|
on reverse-engineering, so not entirely accurate. In particular,
|
||||||
|
various fields are incorrectly assumed to be int-sized which is
|
||||||
|
not true in the general case.
|
||||||
|
|
||||||
|
* https://devblogs.microsoft.com/oldnewthing/20040209-00/?p=40713
|
||||||
|
Describes the MSVC implementation of pointers to member
|
||||||
|
functions for classes with single or multiple inheritance. Does
|
||||||
|
not mention the additional variants for virtual or unknown
|
||||||
|
inheritance. Incorrectly states that the "this" pointer
|
||||||
|
displacement is a size_t when in reality it is an int (important
|
||||||
|
for 64-bit architectures).
|
||||||
|
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
#ifndef MAME_UTIL_DELEGATE_H
|
#ifndef MAME_LIB_UTIL_DELEGATE_H
|
||||||
#define MAME_UTIL_DELEGATE_H
|
#define MAME_LIB_UTIL_DELEGATE_H
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -102,8 +118,8 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <exception>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@ -120,26 +136,26 @@
|
|||||||
|
|
||||||
// select which one we will be using
|
// select which one we will be using
|
||||||
#if defined(MAME_DELEGATE_FORCE_COMPATIBLE)
|
#if defined(MAME_DELEGATE_FORCE_COMPATIBLE)
|
||||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__)
|
||||||
// 32bit MINGW asks for different convention
|
// 32bit MINGW asks for different convention
|
||||||
#if defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__)
|
#if defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__)
|
||||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_ITANIUM
|
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_ITANIUM
|
||||||
#define MAME_DELEGATE_MEMBER_ABI __thiscall
|
#define MAME_DELEGATE_MEMBER_ABI __thiscall
|
||||||
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 1
|
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 1
|
||||||
#elif defined(__clang__) && defined(__i386__) && defined(_WIN32)
|
#elif defined(__clang__) && defined(__i386__) && defined(_WIN32)
|
||||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
||||||
#else
|
#else
|
||||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_ITANIUM
|
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_ITANIUM
|
||||||
#define MAME_DELEGATE_MEMBER_ABI
|
#define MAME_DELEGATE_MEMBER_ABI
|
||||||
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
||||||
#endif
|
#endif
|
||||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||||
#define MAME_DELEGATE_MEMBER_ABI
|
#define MAME_DELEGATE_MEMBER_ABI
|
||||||
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
||||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_MSVC
|
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_MSVC
|
||||||
#else
|
#else
|
||||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__)
|
#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__)
|
||||||
@ -152,12 +168,45 @@
|
|||||||
#define MAME_DELEGATE_ITANIUM_ARM 0
|
#define MAME_DELEGATE_ITANIUM_ARM 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE
|
#if MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE
|
||||||
#define MAME_DELEGATE_MEMBER_ABI
|
#define MAME_DELEGATE_MEMBER_ABI
|
||||||
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief Base for objects used with late binding
|
||||||
|
///
|
||||||
|
/// Default polymorphic class used as base for objects that can be bound
|
||||||
|
/// to after the target function has already been set.
|
||||||
|
class delegate_late_bind
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~delegate_late_bind() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief Inappropriate late bind object error
|
||||||
|
///
|
||||||
|
/// Thrown as an exception if the object supplied for late binding
|
||||||
|
/// cannot be cast to the target type for the delegate's function.
|
||||||
|
class binding_type_exception : public std::bad_cast
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
binding_type_exception(std::type_info const &target_type, std::type_info const &actual_type);
|
||||||
|
|
||||||
|
virtual char const *what() const noexcept override;
|
||||||
|
|
||||||
|
std::type_info const &target_type() const noexcept { return *m_target_type; }
|
||||||
|
std::type_info const &actual_type() const noexcept { return *m_actual_type; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_what;
|
||||||
|
std::type_info const *m_target_type;
|
||||||
|
std::type_info const *m_actual_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace util::detail {
|
namespace util::detail {
|
||||||
|
|
||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
@ -172,7 +221,6 @@ using delegate_generic_function = void(*)();
|
|||||||
|
|
||||||
// define a dummy generic class that is just straight single-inheritance
|
// define a dummy generic class that is just straight single-inheritance
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
class __single_inheritance generic_class;
|
|
||||||
class delegate_generic_class { };
|
class delegate_generic_class { };
|
||||||
#else
|
#else
|
||||||
class delegate_generic_class;
|
class delegate_generic_class;
|
||||||
@ -343,13 +391,13 @@ public:
|
|||||||
{
|
{
|
||||||
return (m_function == rhs.m_function) && (m_this_delta == rhs.m_this_delta);
|
return (m_function == rhs.m_function) && (m_this_delta == rhs.m_this_delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isnull() const
|
bool isnull() const
|
||||||
{
|
{
|
||||||
#if MAME_DELEGATE_ITANIUM_ARM
|
if (MAME_DELEGATE_ITANIUM_ARM)
|
||||||
return !reinterpret_cast<void (*)()>(m_function) && !(m_this_delta & 1);
|
return !reinterpret_cast<void (*)()>(m_function) && !(m_this_delta & 1);
|
||||||
#else
|
else
|
||||||
return !reinterpret_cast<void (*)()>(m_function);
|
return !reinterpret_cast<void (*)()>(m_function);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
@ -446,15 +494,7 @@ public:
|
|||||||
void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
|
void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
|
||||||
{
|
{
|
||||||
funcptr = reinterpret_cast<FunctionType>(m_function);
|
funcptr = reinterpret_cast<FunctionType>(m_function);
|
||||||
std::uint8_t *byteptr = reinterpret_cast<std::uint8_t *>(object);
|
return adjust_this_pointer(object);
|
||||||
if ((sizeof(unknown_base_equiv) == m_size) && m_vt_index)
|
|
||||||
{
|
|
||||||
std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(byteptr + m_vptr_offs);
|
|
||||||
byteptr += *reinterpret_cast<int const *>(vptr + m_vt_index);
|
|
||||||
}
|
|
||||||
if (sizeof(single_base_equiv) < m_size)
|
|
||||||
byteptr += m_this_delta;
|
|
||||||
object = reinterpret_cast<delegate_generic_class *>(byteptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FunctionType>
|
template <typename FunctionType>
|
||||||
@ -463,6 +503,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// adjust the object pointer
|
||||||
|
void adjust_this_pointer(delegate_generic_class *&object) const;
|
||||||
|
|
||||||
// actual state
|
// actual state
|
||||||
uintptr_t m_function = 0; // pointer to function or non-virtual thunk for virtual function call
|
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_this_delta = 0; // delta to apply to the 'this' pointer for multiple inheritance
|
||||||
@ -474,57 +517,75 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE
|
#if MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE
|
||||||
using delegate_mfp = delegate_mfp_compatible;
|
using delegate_mfp = delegate_mfp_compatible;
|
||||||
#elif USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_ITANIUM
|
#elif MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_ITANIUM
|
||||||
using delegate_mfp = delegate_mfp_itanium;
|
using delegate_mfp = delegate_mfp_itanium;
|
||||||
#elif USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_MSVC
|
#elif MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_MSVC
|
||||||
using delegate_mfp = delegate_mfp_msvc;
|
using delegate_mfp = delegate_mfp_msvc;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace util::detail
|
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief Helper class for generating late bind functions
|
||||||
// ======================> delegate_late_bind
|
///
|
||||||
|
/// Members of this class don't depend on the delegate's signature.
|
||||||
// simple polymorphic class that must be mixed into any object that is late-bound
|
/// Keeping them here reduces the number of template instantiations as
|
||||||
class delegate_late_bind
|
/// you'll only need one late bind helper for each class used for late
|
||||||
|
/// binding, not for each class for each delegate signature.
|
||||||
|
template <class LateBindBase>
|
||||||
|
class delegate_late_bind_helper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
delegate_late_bind() { }
|
// make it default constructible and copyable
|
||||||
virtual ~delegate_late_bind() { }
|
delegate_late_bind_helper() = default;
|
||||||
|
delegate_late_bind_helper(delegate_late_bind_helper const &) = default;
|
||||||
|
delegate_late_bind_helper(delegate_late_bind_helper &&) = default;
|
||||||
|
delegate_late_bind_helper &operator=(delegate_late_bind_helper const &) = default;
|
||||||
|
delegate_late_bind_helper &operator=(delegate_late_bind_helper &&) = default;
|
||||||
|
|
||||||
|
template <class FunctionClass>
|
||||||
|
delegate_late_bind_helper(FunctionClass *)
|
||||||
|
: m_latebinder(&delegate_late_bind_helper::late_bind_helper<FunctionClass>)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate_generic_class *operator()(LateBindBase &object) { return m_latebinder(object); }
|
||||||
|
|
||||||
|
explicit operator bool() const noexcept { return bool(m_latebinder); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
using late_bind_func = delegate_generic_class*(*)(LateBindBase &object);
|
||||||
|
|
||||||
|
template <class FunctionClass> static delegate_generic_class *late_bind_helper(LateBindBase &object);
|
||||||
|
|
||||||
|
late_bind_func m_latebinder = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// ======================> binding_type_exception
|
template <class LateBindBase>
|
||||||
|
template <class FunctionClass>
|
||||||
// exception that is thrown when a bind fails the dynamic_cast
|
delegate_generic_class *delegate_late_bind_helper<LateBindBase>::late_bind_helper(LateBindBase &object)
|
||||||
class binding_type_exception : public std::exception
|
|
||||||
{
|
{
|
||||||
public:
|
FunctionClass *result = dynamic_cast<FunctionClass *>(&object);
|
||||||
binding_type_exception(const std::type_info &target_type, const std::type_info &actual_type)
|
if (result)
|
||||||
: m_target_type(target_type), m_actual_type(actual_type)
|
return reinterpret_cast<delegate_generic_class *>(result);
|
||||||
{ }
|
throw binding_type_exception(typeid(FunctionClass), typeid(object));
|
||||||
const std::type_info &m_target_type;
|
}
|
||||||
const std::type_info &m_actual_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
// COMMON DELEGATE BASE CLASS
|
// COMMON DELEGATE BASE CLASS
|
||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
|
|
||||||
// ======================> delegate_base
|
template <class LateBindBase, typename ReturnType, typename... Params>
|
||||||
|
|
||||||
// general delegate class template supporting up to 5 parameters
|
|
||||||
template <typename ReturnType, typename... Params>
|
|
||||||
class delegate_base
|
class delegate_base
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// define our traits
|
// define our traits
|
||||||
template <class FunctionClass> using traits = util::detail::delegate_traits<FunctionClass, ReturnType, Params...>;
|
template <class FunctionClass> using traits = delegate_traits<FunctionClass, ReturnType, Params...>;
|
||||||
using generic_static_func = typename traits<util::detail::delegate_generic_class>::static_func_type;
|
using generic_static_func = typename traits<delegate_generic_class>::static_func_type;
|
||||||
typedef MAME_DELEGATE_MEMBER_ABI generic_static_func generic_member_func;
|
typedef MAME_DELEGATE_MEMBER_ABI generic_static_func generic_member_func;
|
||||||
|
|
||||||
// generic constructor
|
// generic constructor
|
||||||
@ -543,7 +604,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy constructor with late bind
|
// copy constructor with late bind
|
||||||
delegate_base(const delegate_base &src, delegate_late_bind &object)
|
delegate_base(const delegate_base &src, LateBindBase &object)
|
||||||
: m_function(src.m_function)
|
: m_function(src.m_function)
|
||||||
, m_latebinder(src.m_latebinder)
|
, m_latebinder(src.m_latebinder)
|
||||||
, m_raw_function(src.m_raw_function)
|
, m_raw_function(src.m_raw_function)
|
||||||
@ -555,15 +616,16 @@ public:
|
|||||||
// construct from member function with object pointer
|
// construct from member function with object pointer
|
||||||
template <class FunctionClass>
|
template <class FunctionClass>
|
||||||
delegate_base(typename traits<FunctionClass>::member_func_type funcptr, FunctionClass *object)
|
delegate_base(typename traits<FunctionClass>::member_func_type funcptr, FunctionClass *object)
|
||||||
: m_latebinder(&late_bind_helper<FunctionClass>)
|
: m_latebinder(object)
|
||||||
, m_raw_mfp(funcptr, object, static_cast<ReturnType *>(nullptr), static_cast<generic_static_func>(nullptr))
|
, m_raw_mfp(funcptr, object, static_cast<ReturnType *>(nullptr), static_cast<generic_static_func>(nullptr))
|
||||||
{
|
{
|
||||||
bind(object);
|
bind(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// construct from const member function with object pointer
|
||||||
template <class FunctionClass>
|
template <class FunctionClass>
|
||||||
delegate_base(typename traits<FunctionClass>::const_member_func_type funcptr, FunctionClass *object)
|
delegate_base(typename traits<FunctionClass>::const_member_func_type funcptr, FunctionClass *object)
|
||||||
: m_latebinder(&late_bind_helper<FunctionClass>)
|
: m_latebinder(object)
|
||||||
, m_raw_mfp(funcptr, object, static_cast<ReturnType *>(nullptr), static_cast<generic_static_func>(nullptr))
|
, m_raw_mfp(funcptr, object, static_cast<ReturnType *>(nullptr), static_cast<generic_static_func>(nullptr))
|
||||||
{
|
{
|
||||||
bind(object);
|
bind(object);
|
||||||
@ -573,7 +635,7 @@ public:
|
|||||||
template <class FunctionClass>
|
template <class FunctionClass>
|
||||||
delegate_base(typename traits<FunctionClass>::static_ref_func_type funcptr, FunctionClass *object)
|
delegate_base(typename traits<FunctionClass>::static_ref_func_type funcptr, FunctionClass *object)
|
||||||
: m_function(reinterpret_cast<generic_static_func>(funcptr))
|
: m_function(reinterpret_cast<generic_static_func>(funcptr))
|
||||||
, m_latebinder(&late_bind_helper<FunctionClass>)
|
, m_latebinder(object)
|
||||||
, m_raw_function(reinterpret_cast<generic_static_func>(funcptr))
|
, m_raw_function(reinterpret_cast<generic_static_func>(funcptr))
|
||||||
{
|
{
|
||||||
bind(object);
|
bind(object);
|
||||||
@ -602,7 +664,6 @@ public:
|
|||||||
return (m_raw_function == rhs.m_raw_function) && (object() == rhs.object()) && (m_raw_mfp == rhs.m_raw_mfp);
|
return (m_raw_function == rhs.m_raw_function) && (object() == rhs.object()) && (m_raw_mfp == rhs.m_raw_mfp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// call the function
|
// call the function
|
||||||
ReturnType operator()(Params... args) const
|
ReturnType operator()(Params... args) const
|
||||||
{
|
{
|
||||||
@ -614,41 +675,25 @@ public:
|
|||||||
|
|
||||||
// getters
|
// getters
|
||||||
bool has_object() const { return object() != nullptr; }
|
bool has_object() const { return object() != nullptr; }
|
||||||
|
|
||||||
// helpers
|
|
||||||
bool isnull() const { return !m_raw_function && m_raw_mfp.isnull(); }
|
bool isnull() const { return !m_raw_function && m_raw_mfp.isnull(); }
|
||||||
bool is_mfp() const { return !m_raw_mfp.isnull(); }
|
bool is_mfp() const { return !m_raw_mfp.isnull(); }
|
||||||
|
|
||||||
// late binding
|
// late binding
|
||||||
void late_bind(delegate_late_bind &object)
|
void late_bind(LateBindBase &object)
|
||||||
{
|
{
|
||||||
if (m_latebinder)
|
if (m_latebinder)
|
||||||
bind((*m_latebinder)(object));
|
bind(m_latebinder(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// return the actual object (not the one we use for calling)
|
// return the actual object (not the one we use for calling)
|
||||||
util::detail::delegate_generic_class *object() const { return is_mfp() ? m_raw_mfp.real_object(m_object) : m_object; }
|
delegate_generic_class *object() const { return is_mfp() ? m_raw_mfp.real_object(m_object) : m_object; }
|
||||||
|
|
||||||
// late binding function
|
|
||||||
using late_bind_func = util::detail::delegate_generic_class*(*)(delegate_late_bind &object);
|
|
||||||
|
|
||||||
// late binding helper
|
|
||||||
template <class FunctionClass>
|
|
||||||
static util::detail::delegate_generic_class *late_bind_helper(delegate_late_bind &object)
|
|
||||||
{
|
|
||||||
FunctionClass *result = dynamic_cast<FunctionClass *>(&object);
|
|
||||||
if (!result)
|
|
||||||
throw binding_type_exception(typeid(FunctionClass), typeid(object));
|
|
||||||
|
|
||||||
return reinterpret_cast<util::detail::delegate_generic_class *>(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind the actual object
|
// bind the actual object
|
||||||
template <typename FunctionClass>
|
template <typename FunctionClass>
|
||||||
void bind(FunctionClass *object)
|
void bind(FunctionClass *object)
|
||||||
{
|
{
|
||||||
m_object = reinterpret_cast<util::detail::delegate_generic_class *>(object);
|
m_object = reinterpret_cast<delegate_generic_class *>(object);
|
||||||
|
|
||||||
// if we're wrapping a member function pointer, handle special stuff
|
// if we're wrapping a member function pointer, handle special stuff
|
||||||
if (m_object && is_mfp())
|
if (m_object && is_mfp())
|
||||||
@ -657,12 +702,14 @@ protected:
|
|||||||
|
|
||||||
// internal state
|
// internal state
|
||||||
generic_static_func m_function = nullptr; // resolved static function pointer
|
generic_static_func m_function = nullptr; // resolved static function pointer
|
||||||
util::detail::delegate_generic_class * m_object = nullptr; // resolved object to the post-cast object
|
delegate_generic_class * m_object = nullptr; // resolved object to the post-cast object
|
||||||
late_bind_func m_latebinder = nullptr; // late binding helper
|
delegate_late_bind_helper<LateBindBase> m_latebinder; // late binding helper
|
||||||
generic_static_func m_raw_function = nullptr; // raw static function pointer
|
generic_static_func m_raw_function = nullptr; // raw static function pointer
|
||||||
util::detail::delegate_mfp m_raw_mfp; // raw member function pointer
|
delegate_mfp m_raw_mfp; // raw member function pointer
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace util::detail
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
@ -670,13 +717,13 @@ protected:
|
|||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
|
|
||||||
// declare the base template
|
// declare the base template
|
||||||
template <typename Signature> class delegate;
|
template <typename Signature, class LateBindBase = delegate_late_bind> class delegate;
|
||||||
|
|
||||||
template <typename ReturnType, typename... Params>
|
template <class LateBindBase, typename ReturnType, typename... Params>
|
||||||
class delegate<ReturnType (Params...)> : public delegate_base<ReturnType, Params...>
|
class delegate<ReturnType (Params...), LateBindBase> : public util::detail::delegate_base<LateBindBase, ReturnType, Params...>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
using basetype = delegate_base<ReturnType, Params...>;
|
using basetype = util::detail::delegate_base<LateBindBase, ReturnType, Params...>;
|
||||||
using functoid_setter = void (*)(delegate &);
|
using functoid_setter = void (*)(delegate &);
|
||||||
|
|
||||||
template <typename T> struct functoid_type_unwrap { using type = std::remove_reference_t<T>; };
|
template <typename T> struct functoid_type_unwrap { using type = std::remove_reference_t<T>; };
|
||||||
@ -772,7 +819,7 @@ public:
|
|||||||
m_set_functoid(*this);
|
m_set_functoid(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate(delegate const &src, delegate_late_bind &object)
|
delegate(delegate const &src, LateBindBase &object)
|
||||||
: basetype(src.m_functoid.has_value() ? basetype() : basetype(src, object))
|
: basetype(src.m_functoid.has_value() ? basetype() : basetype(src, object))
|
||||||
, m_functoid(src.m_functoid)
|
, m_functoid(src.m_functoid)
|
||||||
, m_set_functoid(src.m_set_functoid)
|
, m_set_functoid(src.m_set_functoid)
|
||||||
@ -830,4 +877,4 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAME_UTIL_DELEGATE_H
|
#endif // MAME_LIB_UTIL_DELEGATE_H
|
||||||
|
@ -204,7 +204,7 @@ _recip_approx(float value)
|
|||||||
multiply and return the full 128 bit result
|
multiply and return the full 128 bit result
|
||||||
-------------------------------------------------*/
|
-------------------------------------------------*/
|
||||||
|
|
||||||
#if defined(__ppc64__)
|
#if defined(__ppc64__) || defined(__PPC64___) || defined(_ARCH_PPC64)
|
||||||
#define mul_64x64 _mul_64x64
|
#define mul_64x64 _mul_64x64
|
||||||
inline int64_t ATTR_FORCE_INLINE
|
inline int64_t ATTR_FORCE_INLINE
|
||||||
_mul_64x64(int64_t a, int64_t b, int64_t &hi)
|
_mul_64x64(int64_t a, int64_t b, int64_t &hi)
|
||||||
@ -221,7 +221,7 @@ _mul_64x64(int64_t a, int64_t b, int64_t &hi)
|
|||||||
bit multiply and return the full 128 bit result
|
bit multiply and return the full 128 bit result
|
||||||
-------------------------------------------------*/
|
-------------------------------------------------*/
|
||||||
|
|
||||||
#if defined(__ppc64__)
|
#if defined(__ppc64__) || defined(__PPC64___) || defined(_ARCH_PPC64)
|
||||||
#define mulu_64x64 _mulu_64x64
|
#define mulu_64x64 _mulu_64x64
|
||||||
inline uint64_t ATTR_FORCE_INLINE
|
inline uint64_t ATTR_FORCE_INLINE
|
||||||
_mulu_64x64(uint64_t a, uint64_t b, uint64_t &hi)
|
_mulu_64x64(uint64_t a, uint64_t b, uint64_t &hi)
|
||||||
@ -285,7 +285,7 @@ _count_leading_ones_32(uint32_t value)
|
|||||||
leading zero bits in a 64-bit value
|
leading zero bits in a 64-bit value
|
||||||
-------------------------------------------------*/
|
-------------------------------------------------*/
|
||||||
|
|
||||||
#if defined(__ppc64__)
|
#if defined(__ppc64__) || defined(__PPC64___) || defined(_ARCH_PPC64)
|
||||||
#define count_leading_zeros_64 _count_leading_zeros_64
|
#define count_leading_zeros_64 _count_leading_zeros_64
|
||||||
inline uint8_t ATTR_CONST ATTR_FORCE_INLINE
|
inline uint8_t ATTR_CONST ATTR_FORCE_INLINE
|
||||||
_count_leading_zeros_64(uint64_t value)
|
_count_leading_zeros_64(uint64_t value)
|
||||||
@ -308,7 +308,7 @@ _count_leading_zeros_64(uint64_t value)
|
|||||||
leading one bits in a 64-bit value
|
leading one bits in a 64-bit value
|
||||||
-------------------------------------------------*/
|
-------------------------------------------------*/
|
||||||
|
|
||||||
#if defined(__ppc64__)
|
#if defined(__ppc64__) || defined(__PPC64___) || defined(_ARCH_PPC64)
|
||||||
#define count_leading_ones_64 _count_leading_ones_64
|
#define count_leading_ones_64 _count_leading_ones_64
|
||||||
inline uint8_t ATTR_CONST ATTR_FORCE_INLINE
|
inline uint8_t ATTR_CONST ATTR_FORCE_INLINE
|
||||||
_count_leading_ones_64(uint64_t value)
|
_count_leading_ones_64(uint64_t value)
|
||||||
|
Loading…
Reference in New Issue
Block a user