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:
Vas Crabb 2021-09-20 03:03:26 +10:00
parent 566a741f4f
commit 401b22bbf5
6 changed files with 1040 additions and 481 deletions

View File

@ -414,7 +414,7 @@ int running_machine::run(bool quiet)
}
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;
}
catch (tag_add_exception &aex)

File diff suppressed because it is too large Load Diff

View File

@ -60,7 +60,6 @@ private:
using string_set = std::unordered_set<std::string>;
// internal helpers
const char *ioport_string_from_index(u32 index);
int get_defstr_index(const char *string, bool suppress_error = false);
// core helpers
@ -69,9 +68,6 @@ private:
void validate_one(const game_driver &driver);
// internal sub-checks
void validate_core();
void validate_inlines();
void validate_rgb();
void validate_driver(device_t &root);
void validate_roms(device_t &root);
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);
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
driver_enumerator m_drivlist;

View File

@ -11,31 +11,54 @@
#include "delegate.h"
#include <cstdio>
#include <sstream>
//**************************************************************************
// MACROS
//**************************************************************************
#if defined(LOG_DELEGATES)
#define LOG(...) printf(__VA_ARGS__)
#if defined(MAME_DELEGATE_LOG_ADJ)
#define LOG(...) printf(__VA_ARGS__)
#else
#define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false)
#define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false)
#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
// 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
#define MAME_DELEGATE_VT_DESCRIPTOR 1
#endif
#ifndef MAME_DELEGATE_VT_DESCRIPTOR
#define MAME_DELEGATE_VT_DESCRIPTOR 0
#define MAME_DELEGATE_VT_DESCRIPTOR 0
#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 {
//**************************************************************************
@ -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
// and return the actual final code pointer
//-------------------------------------------------//
// delegate_mfp_itanium::convert_to_generic -
// 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
{
#if MAME_DELEGATE_ITANIUM_ARM
// apply the "this" delta to the object first - remember to shift right one bit position
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> 1));
if (!(m_this_delta & 1))
// apply the "this" delta to the object first - the value is shifted to the left one bit position for the ARM-like variant
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 >> (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
LOG("Calculated Addr = %p\n", reinterpret_cast<void const *>(m_function));
// conventional function pointer
LOG("ptr=%p\n", reinterpret_cast<void const *>(m_function));
return reinterpret_cast<delegate_generic_function>(m_function);
}
else
{
// otherwise, it is the byte index into the vtable where the actual function lives
std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function;
#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)));
// 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 - (MAME_DELEGATE_ITANIUM_ARM ? 0 : 1);
delegate_generic_function result;
if (MAME_DELEGATE_VT_DESCRIPTOR)
result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr));
else
result = *reinterpret_cast<delegate_generic_function const *>(vtable_ptr);
LOG("ptr=%p (vtable)\n", reinterpret_cast<void const *>(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))
}
//-------------------------------------------------
// 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)
{
// 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);
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);
}
else
{
// otherwise, it is the byte index into the vtable where the actual function lives
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;
}
#endif // MAME_DELEGATE_ITANIUM_ARM
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

View File

@ -33,7 +33,7 @@
The "compatible" version of delegates is based on an implementation
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
for each target function. The static function takes the first
@ -75,26 +75,42 @@
func(object, p1, p2).
Pros:
* as fast as a standard function call in static and member cases
* no stub functions or double-hops needed
* as fast as a standard function call in static and non-virtual
member cases
* no stub functions needed
Cons:
* requires internal knowledge of the member function pointer
* 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
* virtual member function lookup cannot be done in advance
--------------------------------------------------------------------
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
#define MAME_UTIL_DELEGATE_H
#ifndef MAME_LIB_UTIL_DELEGATE_H
#define MAME_LIB_UTIL_DELEGATE_H
#pragma once
@ -102,8 +118,8 @@
#include <cassert>
#include <cstdint>
#include <cstring>
#include <exception>
#include <functional>
#include <string>
#include <type_traits>
#include <typeinfo>
#include <utility>
@ -120,26 +136,26 @@
// select which one we will be using
#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__)
// 32bit MINGW asks for different convention
#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_DIFFERENT_MEMBER_ABI 1
#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
#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_DIFFERENT_MEMBER_ABI 0
#endif
#elif defined(_MSC_VER) && defined(_M_X64)
#define MAME_DELEGATE_MEMBER_ABI
#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
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
#endif
#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__)
@ -152,12 +168,45 @@
#define MAME_DELEGATE_ITANIUM_ARM 0
#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_DIFFERENT_MEMBER_ABI 0
#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 {
//**************************************************************************
@ -172,7 +221,6 @@ using delegate_generic_function = void(*)();
// define a dummy generic class that is just straight single-inheritance
#ifdef _MSC_VER
class __single_inheritance generic_class;
class delegate_generic_class { };
#else
class delegate_generic_class;
@ -343,13 +391,13 @@ public:
{
return (m_function == rhs.m_function) && (m_this_delta == rhs.m_this_delta);
}
bool isnull() const
{
#if MAME_DELEGATE_ITANIUM_ARM
return !reinterpret_cast<void (*)()>(m_function) && !(m_this_delta & 1);
#else
return !reinterpret_cast<void (*)()>(m_function);
#endif
if (MAME_DELEGATE_ITANIUM_ARM)
return !reinterpret_cast<void (*)()>(m_function) && !(m_this_delta & 1);
else
return !reinterpret_cast<void (*)()>(m_function);
}
// getters
@ -446,15 +494,7 @@ public:
void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
{
funcptr = reinterpret_cast<FunctionType>(m_function);
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;
object = reinterpret_cast<delegate_generic_class *>(byteptr);
return adjust_this_pointer(object);
}
template <typename FunctionType>
@ -463,6 +503,9 @@ public:
}
private:
// adjust the object pointer
void adjust_this_pointer(delegate_generic_class *&object) const;
// actual state
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
@ -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;
#elif USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_ITANIUM
#elif MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_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;
#endif
} // namespace util::detail
// ======================> delegate_late_bind
// simple polymorphic class that must be mixed into any object that is late-bound
class delegate_late_bind
/// \brief Helper class for generating late bind functions
///
/// Members of this class don't depend on the delegate's signature.
/// Keeping them here reduces the number of template instantiations as
/// 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:
delegate_late_bind() { }
virtual ~delegate_late_bind() { }
// make it default constructible and copyable
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
// exception that is thrown when a bind fails the dynamic_cast
class binding_type_exception : public std::exception
template <class LateBindBase>
template <class FunctionClass>
delegate_generic_class *delegate_late_bind_helper<LateBindBase>::late_bind_helper(LateBindBase &object)
{
public:
binding_type_exception(const std::type_info &target_type, const std::type_info &actual_type)
: m_target_type(target_type), m_actual_type(actual_type)
{ }
const std::type_info &m_target_type;
const std::type_info &m_actual_type;
};
FunctionClass *result = dynamic_cast<FunctionClass *>(&object);
if (result)
return reinterpret_cast<delegate_generic_class *>(result);
throw binding_type_exception(typeid(FunctionClass), typeid(object));
}
//**************************************************************************
// COMMON DELEGATE BASE CLASS
//**************************************************************************
// ======================> delegate_base
// general delegate class template supporting up to 5 parameters
template <typename ReturnType, typename... Params>
template <class LateBindBase, typename ReturnType, typename... Params>
class delegate_base
{
public:
// define our traits
template <class FunctionClass> using traits = util::detail::delegate_traits<FunctionClass, ReturnType, Params...>;
using generic_static_func = typename traits<util::detail::delegate_generic_class>::static_func_type;
template <class FunctionClass> using traits = delegate_traits<FunctionClass, ReturnType, Params...>;
using generic_static_func = typename traits<delegate_generic_class>::static_func_type;
typedef MAME_DELEGATE_MEMBER_ABI generic_static_func generic_member_func;
// generic constructor
@ -543,7 +604,7 @@ public:
}
// 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_latebinder(src.m_latebinder)
, m_raw_function(src.m_raw_function)
@ -555,15 +616,16 @@ public:
// construct from member function with object pointer
template <class FunctionClass>
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))
{
bind(object);
}
// construct from const member function with object pointer
template <class FunctionClass>
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))
{
bind(object);
@ -573,7 +635,7 @@ public:
template <class FunctionClass>
delegate_base(typename traits<FunctionClass>::static_ref_func_type funcptr, FunctionClass *object)
: 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))
{
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);
}
// call the function
ReturnType operator()(Params... args) const
{
@ -614,41 +675,25 @@ public:
// getters
bool has_object() const { return object() != nullptr; }
// helpers
bool isnull() const { return !m_raw_function && m_raw_mfp.isnull(); }
bool is_mfp() const { return !m_raw_mfp.isnull(); }
// late binding
void late_bind(delegate_late_bind &object)
void late_bind(LateBindBase &object)
{
if (m_latebinder)
bind((*m_latebinder)(object));
bind(m_latebinder(object));
}
protected:
// 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; }
// 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);
}
delegate_generic_class *object() const { return is_mfp() ? m_raw_mfp.real_object(m_object) : m_object; }
// bind the actual object
template <typename FunctionClass>
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 (m_object && is_mfp())
@ -657,12 +702,14 @@ protected:
// internal state
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
late_bind_func m_latebinder = nullptr; // late binding helper
delegate_generic_class * m_object = nullptr; // resolved object to the post-cast object
delegate_late_bind_helper<LateBindBase> m_latebinder; // late binding helper
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
template <typename Signature> class delegate;
template <typename Signature, class LateBindBase = delegate_late_bind> class delegate;
template <typename ReturnType, typename... Params>
class delegate<ReturnType (Params...)> : public delegate_base<ReturnType, Params...>
template <class LateBindBase, typename ReturnType, typename... Params>
class delegate<ReturnType (Params...), LateBindBase> : public util::detail::delegate_base<LateBindBase, ReturnType, Params...>
{
private:
using basetype = delegate_base<ReturnType, Params...>;
using basetype = util::detail::delegate_base<LateBindBase, ReturnType, Params...>;
using functoid_setter = void (*)(delegate &);
template <typename T> struct functoid_type_unwrap { using type = std::remove_reference_t<T>; };
@ -772,7 +819,7 @@ public:
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))
, m_functoid(src.m_functoid)
, m_set_functoid(src.m_set_functoid)
@ -830,4 +877,4 @@ public:
}
};
#endif // MAME_UTIL_DELEGATE_H
#endif // MAME_LIB_UTIL_DELEGATE_H

View File

@ -204,7 +204,7 @@ _recip_approx(float value)
multiply and return the full 128 bit result
-------------------------------------------------*/
#if defined(__ppc64__)
#if defined(__ppc64__) || defined(__PPC64___) || defined(_ARCH_PPC64)
#define mul_64x64 _mul_64x64
inline int64_t ATTR_FORCE_INLINE
_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
-------------------------------------------------*/
#if defined(__ppc64__)
#if defined(__ppc64__) || defined(__PPC64___) || defined(_ARCH_PPC64)
#define mulu_64x64 _mulu_64x64
inline uint64_t ATTR_FORCE_INLINE
_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
-------------------------------------------------*/
#if defined(__ppc64__)
#if defined(__ppc64__) || defined(__PPC64___) || defined(_ARCH_PPC64)
#define count_leading_zeros_64 _count_leading_zeros_64
inline uint8_t ATTR_CONST ATTR_FORCE_INLINE
_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
-------------------------------------------------*/
#if defined(__ppc64__)
#if defined(__ppc64__) || defined(__PPC64___) || defined(_ARCH_PPC64)
#define count_leading_ones_64 _count_leading_ones_64
inline uint8_t ATTR_CONST ATTR_FORCE_INLINE
_count_leading_ones_64(uint64_t value)