-util/delegate.cpp: One less level of indirection for functoid delegates.

* If a delegate is set to a functoid (e.g. a lambda) with a signature
  that is an exact match for the delegate's signature, it will be bound
  directly.  If arguments or the return value need conversion, a static
  adaptor will be generated.  This removes unnecessary indirection
  through std::function::operator().

-Added a few more documentation comments.
This commit is contained in:
Vas Crabb 2021-09-16 00:28:09 +10:00 committed by Vas Crabb
parent 4a39d895af
commit d334586156
8 changed files with 320 additions and 96 deletions

View File

@ -127,7 +127,6 @@ files{
MAME_DIR .. "src/mame/includes/exidy.h",
MAME_DIR .. "src/mame/audio/exidy.cpp",
MAME_DIR .. "src/mame/audio/exidy.h",
MAME_DIR .. "src/mame/video/exidy.cpp",
MAME_DIR .. "src/mame/audio/exidy440.cpp",
MAME_DIR .. "src/mame/audio/exidy440.h",
MAME_DIR .. "src/mame/audio/nl_fireone.h",

View File

@ -83,6 +83,8 @@ protected:
template <class FunctionClass> using const_member_func_type = typename basetype::template const_member_func_type<FunctionClass>;
template <class FunctionClass> using static_ref_func_type = typename basetype::template static_ref_func_type<FunctionClass>;
template <typename T> using suitable_functoid = typename basetype::template suitable_functoid<T>;
public:
// create a standard set of constructors
named_delegate() = default;
@ -91,12 +93,18 @@ public:
template <class FunctionClass> named_delegate(member_func_type<FunctionClass> funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { }
template <class FunctionClass> named_delegate(const_member_func_type<FunctionClass> funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { }
template <class FunctionClass> named_delegate(static_ref_func_type<FunctionClass> funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { }
template <typename T> named_delegate(T &&funcptr, std::enable_if_t<std::is_constructible<std::function<Signature>, T>::value, char const *> name) : basetype(std::forward<T>(funcptr)), m_name(name) { }
template <typename T> named_delegate(T &&funcptr, std::enable_if_t<suitable_functoid<T>::value, char const *> name) : basetype(std::forward<T>(funcptr)), m_name(name) { }
// allow assignment
named_delegate &operator=(named_delegate const &src) = default;
named_delegate &operator=(named_delegate &&src) = default;
named_delegate &operator=(std::nullptr_t) noexcept { reset(); return *this; }
// getters
char const *name() const { return m_name; }
// unsetter
void reset() noexcept { basetype::reset(); m_name = nullptr; }
};
// ======================> device_delegate
@ -111,6 +119,9 @@ class device_delegate<ReturnType (Params...)> : protected named_delegate<ReturnT
private:
using basetype = named_delegate<ReturnType (Params...)>;
template <typename T>
using suitable_functoid = typename basetype::template suitable_functoid<T>;
template <class T, class U>
using is_related_device_implementation = std::bool_constant<std::is_base_of_v<T, U> && std::is_base_of_v<device_t, U> >;
template <class T, class U>
@ -189,7 +200,7 @@ public:
// construct with callable object
template <typename T>
device_delegate(device_t &owner, T &&funcptr, std::enable_if_t<std::is_constructible<std::function<ReturnType (Params...)>, T>::value, char const *> name)
device_delegate(device_t &owner, T &&funcptr, std::enable_if_t<suitable_functoid<T>::value, char const *> name)
: basetype(std::forward<T>(funcptr), name)
, detail::device_delegate_helper(owner)
{ basetype::operator=(basetype(std::forward<T>(funcptr), name)); }
@ -219,12 +230,14 @@ public:
{ basetype::operator=(basetype(funcptr, name, static_cast<D *>(&object))); set_tag(finder_target().first); }
// setter that takes a functoid
template <typename T> std::enable_if_t<std::is_constructible<std::function<ReturnType (Params...)>, T>::value> set(T &&funcptr, char const *name)
template <typename T> std::enable_if_t<suitable_functoid<T>::value> set(T &&funcptr, char const *name)
{ basetype::operator=(basetype(std::forward<T>(funcptr), name)); }
// unsetter
void set(std::nullptr_t)
{ basetype::operator=(basetype()); set_tag(finder_target().first); }
{ reset(); }
void reset()
{ basetype::reset(); set_tag(finder_target().first); }
// perform the binding
void resolve() { if (!basetype::isnull() && !basetype::has_object()) basetype::late_bind(bound_object()); }

View File

@ -353,7 +353,7 @@ constexpr auto driver_device_creator = &emu::detail::driver_tag_func<DriverClass
/// letters and underscores.
/// \param Class The exposed device class name. Must be the device
/// implementation class, or a public base of it, and must be derived
/// from #device_t.
/// from #device_t or #device_interface.
/// \sa DECLARE_DEVICE_TYPE_NS DEFINE_DEVICE_TYPE
/// DEFINE_DEVICE_TYPE_PRIVATE
#define DECLARE_DEVICE_TYPE(Type, Class) \
@ -379,7 +379,8 @@ constexpr auto driver_device_creator = &emu::detail::driver_tag_func<DriverClass
/// containing the exposed device class.
/// \param Class The exposed device class name, without namespace
/// qualifiers. Must be the device implementation class, or a public
/// base of it, and must be derived from #device_t.
/// base of it, and must be derived from #device_t or
/// #device_interface.
/// \sa DECLARE_DEVICE_TYPE DEFINE_DEVICE_TYPE
/// DEFINE_DEVICE_TYPE_PRIVATE
#define DECLARE_DEVICE_TYPE_NS(Type, Namespace, Class) \
@ -435,7 +436,7 @@ constexpr auto driver_device_creator = &emu::detail::driver_tag_func<DriverClass
/// letters and underscores.
/// \param Base The fully-qualified exposed device class name. Must be
/// a public base of the device implementation class, and must be
/// derived from #device_t.
/// derived from #device_t or #device_interface.
/// \param Class The fully-qualified device implementation class name.
/// Must be derived from the exposed device class, and indirectly from
/// #device_t.
@ -457,16 +458,24 @@ constexpr auto driver_device_creator = &emu::detail::driver_tag_func<DriverClass
/// \}
// exception classes
/// \brief Start order dependencies not satisfied exception
///
/// May be thrown from the start member functions of #device_t and
/// #device_interface implementations if start order dependencies have
/// not been satisfied. MAME will start additional devices before
/// reattempting to start the device that threw the exception.
/// \sa device_t::device_start device_interface::interface_pre_start
class device_missing_dependencies : public emu_exception { };
// timer IDs for devices
typedef u32 device_timer_id;
// ======================> device_t
// device_t represents a device
/// \brief Base class for devices
///
/// The base class for all device implementations in MAME's modular
/// architecture.
class device_t : public delegate_late_bind
{
DISABLE_COPYING(device_t);
@ -701,25 +710,102 @@ public:
void synchronize(device_timer_id id = 0, int param = 0, void *ptr = nullptr) { timer_set(attotime::zero, id, param, ptr); }
void timer_expired(emu_timer &timer, device_timer_id id, int param, void *ptr) { device_timer(timer, id, param, ptr); }
// state saving interfaces
template<typename ItemType>
/// \brief Register data for save states
///
/// Registers data to be automatically saved/restored. Can be used
/// with fixed-sized integer types, enumerated types marked safe for
/// saving (see #ALLOW_SAVE_TYPE and #ALLOW_SAVE_TYPE_AND_VECTOR).
/// Supports C arrays, \c std::array and \c std::vector. Note that
/// \c std::vector instances must not be resized after being
/// registered to be saved/restored.
/// \param value Reference to the data to be saved/restored. The
/// \c NAME macro can be used to simplify specifying the
/// \p valname argument at the same time.
/// \param [in] valname The name of the saved item. The combination
/// of the \p valname and \p index arguments must be unique across
/// saved items for a device.
/// \param [in] index A numeric value to distinguish between saved
/// items with the same name.
template <typename ItemType>
void ATTR_COLD save_item(ItemType &value, const char *valname, int index = 0)
{
assert(m_save);
m_save->save_item(this, name(), tag(), index, value, valname);
}
template<typename ItemType, typename StructType, typename ElementType>
/// \brief Register a member of a structure in an array for save
/// states
///
/// Registers data to be automatically saved/restored. Can be used
/// with fixed-sized integer types, enumerated types marked safe for
/// saving (see #ALLOW_SAVE_TYPE and #ALLOW_SAVE_TYPE_AND_VECTOR).
/// Used to allow saving/restoring members of structures in C arrays
/// or \c std::array instances.
/// \param value Reference to the array of structures containing the
/// member to be saved/restored. The #STRUCT_MEMBER macro can be
/// used to simplify specifying the \p element and \p valname
/// arguments at the same time.
/// \param [in] element Pointer to the member of the structure to
/// save/restore.
/// \param [in] valname The name of the saved item. The combination
/// of the \p valname and \p index arguments must be unique across
/// saved items for a device.
/// \param [in] index A numeric value to distinguish between saved
/// items with the same name.
template <typename ItemType, typename StructType, typename ElementType>
void ATTR_COLD save_item(ItemType &value, ElementType StructType::*element, const char *valname, int index = 0)
{
assert(m_save);
m_save->save_item(this, name(), tag(), index, value, element, valname);
}
/// \brief Register an array of indeterminate for save states
///
/// Registers data to be automatically saved/restored when the
/// length of the outermost array cannot be automatically determined
/// at compile time. Can be used with C arrays of indeterminate
/// length, pointers, and \c std::unique_ptr instances pointing to
/// arrays. Use #save_item if length of the array can be determined
/// at compile time.
/// \param value Pointer to the array containing the data to be
/// saved/restored. The \c NAME macro can be used to simplify
/// specifying the \p valname argument at the same time.
/// \param [in] valname The name of the saved item. The combination
/// of the \p valname and \p index arguments must be unique across
/// saved items for a device.
/// \param [in] count The number of elements in the outermost array.
/// \param [in] index A numeric value to distinguish between saved
/// items with the same name.
/// \sa save_item
template<typename ItemType>
void ATTR_COLD save_pointer(ItemType &&value, const char *valname, u32 count, int index = 0)
{
assert(m_save);
m_save->save_pointer(this, name(), tag(), index, std::forward<ItemType>(value), valname, count);
}
/// \brief Register a member of a structure in an array of
/// indeterminate for save states
///
/// Registers data to be automatically saved/restored when the
/// length of the outermost array cannot be automatically determined
/// at compile time. Can be used with C arrays of indeterminate
/// length, pointers, and \c std::unique_ptr instances pointing to
/// arrays. Use #save_item if length of the array can be determined
/// at compile time.
/// \param value Pointer to the array of structures containing the
/// member to be saved/restored. The #STRUCT_MEMBER macro can be
/// used to simplify specifying the \p element and \p valname
/// arguments at the same time.
/// \param [in] element Pointer to the member of the structure to
/// save/restore.
/// \param [in] valname The name of the saved item. The combination
/// of the \p valname and \p index arguments must be unique across
/// saved items for a device.
/// \param [in] count The number of elements in the outermost array.
/// \param [in] index A numeric value to distinguish between saved
/// items with the same name.
/// \sa save_item
template<typename ItemType, typename StructType, typename ElementType>
void ATTR_COLD save_pointer(ItemType &&value, ElementType StructType::*element, const char *valname, u32 count, int index = 0)
{
@ -763,8 +849,8 @@ protected:
///
/// Perform any final configuration tasks after all devices in the
/// system have added machine configuration. This is called after
/// any #device_interface mix-in interface_config_complete members
/// have completed.
/// any #device_interface mix-in \c interface_config_complete
/// members have completed.
///
/// Note that automatic object finders will not have been resolved
/// at the time this member is called.
@ -793,29 +879,29 @@ protected:
/// Implement this member to complete object resolution before any
/// devices are started. For example it may be necessary to resolve
/// callbacks before any devices start so initial input conditions
/// can be set. This is called after all registerd automatic object
/// finders are resolved.
/// can be set. This is called after all registered automatic
/// object finders are resolved.
virtual void device_resolve_objects() ATTR_COLD;
/// \brief Device start handler
///
/// Implement this member to set up the initial state of the device
/// on start. This will be called after all #device_interface
// /mix-in interface_pre_start members have completed successfully.
/// If the device can't start until another device has completed
/// starting, throw a #device_missing_dependencies exception.
/// Starting will be postponed until additional devices have been
/// started.
/// mix-in \c interface_pre_start members have completed
/// successfully. If the device can't start until another device
/// has completed starting, throw a #device_missing_dependencies
/// exception. Starting will be postponed until additional devices
/// have started.
///
/// If a device's base class is not device_t, it's good practice to
/// check start order dependencies (and throw
/// If a device's direct base class is not #device_t, it's good
/// practice to check start order dependencies (and throw
/// #device_missing_dependencies if necessary) before calling the
/// base implementation. This will ensure that the base
/// implementation won't be called twice if starting needs to be
/// postponed.
///
/// This is the correct place to register for save states.
/// \sa device_reset device_stop
/// \sa device_reset device_stop save_item save_pointer
/// device_interface::interface_pre_start
/// device_interface::interface_post_start
virtual void device_start() ATTR_COLD = 0;
@ -824,8 +910,8 @@ protected:
///
/// Implement this member to perform additional tasks on ending an
/// emulation session. You may deallocate memory here. This is
/// called after interface_pre_stop is called for all
/// #device_interface mix-ins, and before interface_post_stop is
/// called after \c interface_pre_stop is called for all
/// #device_interface mix-ins, and before \c interface_post_stop is
/// called for any #device_interface mix-ins.
/// \sa device_interface::interface_pre_stop
/// device_interface::interface_post_stop
@ -834,7 +920,7 @@ protected:
/// \brief Device reset handler
///
/// Implement this member to provide reset behaviour. This is
/// called after all #device_interface mix-in interface_pre_reset
/// called after all #device_interface mix-in \c interface_pre_reset
/// members have completed, and before any child devices are reset.
/// All devices are reset at the beginning of an emulation session
/// (after all devices have been started), and also when the user
@ -859,7 +945,7 @@ protected:
/// after child devices are reset. This is called when resetting a
/// device after #device_reset has been called and all child devices
/// have been reset, and before any #device_interface mix-in
/// interface_post_reset members are called.
/// \c interface_post_reset members are called.
/// \sa device_reset device_interface::interface_pre_reset
/// device_interface::interface_post_reset
virtual void device_reset_after_children() ATTR_COLD;
@ -870,8 +956,10 @@ protected:
/// registered save state items are recorded. For example it may be
/// necessary to flush caches, serialise self-referencing members or
/// pointers into data structures. This is called after all
/// #device_interface mix-in interface_pre_save members are called.
/// \sa device_post_load device_interface::interface_pre_save
/// #device_interface mix-in \c interface_pre_save members are
/// called.
/// \sa device_post_load save_item save_pointer
/// device_interface::interface_pre_save
virtual void device_pre_save() ATTR_COLD;
/// \brief Complete save state loading
@ -880,8 +968,10 @@ protected:
/// registered save state items are loaded. For example it may be
/// necessary to update or invalidate caches, or de-serialise
/// pointers into data structures. This is called after all
/// #device_interface mix-in interface_post_load members are called.
/// \sa device_pre_save device_interface::interface_post_load
/// #device_interface mix-in \c interface_post_load members are
/// called.
/// \sa device_pre_save save_item save_pointer
/// device_interface::interface_post_load
virtual void device_post_load() ATTR_COLD;
virtual void device_clock_changed();
@ -968,7 +1058,7 @@ public:
///
/// Perform any final configuration tasks after all devices in the
/// system have added machine configuration. This is called before
/// device_config_complete is called for the device.
/// \c device_config_complete is called for the device.
///
/// Note that automatic object finders will not have been resolved
/// at this time.
@ -1003,12 +1093,12 @@ public:
/// have been started.
///
/// Note that this member may be called multiple times if another
/// device_interface mix-in throws a #device_missing_dependencies
/// exception from its interface_pre_start member, or if the device
/// throws a #device_missing_dependencies exception from its
/// device_start member. You must check to ensure that operations
/// like resource allocation are not performed multiple times, or
/// postpone them until #interface_post_start is called.
/// \c device_interface mix-in throws a #device_missing_dependencies
/// exception from its \c interface_pre_start member, or if the
/// device throws a #device_missing_dependencies exception from its
/// \c device_start member. You must check to ensure that
/// operations like resource allocation are not performed multiple
/// times, or postpone them until #interface_post_start is called.
///
/// It's simpler to register for save states when
/// #interface_post_start is called.
@ -1019,22 +1109,23 @@ public:
///
/// Implement this member to complete mix-in start-up. This is
/// called after #interface_pre_start is called for all
/// device_interface mix-ins, and after device_start is called for
/// the device. This member will only be called once, it will not
/// be called multiple times if device starting is postponed.
/// device_interface mix-ins, and after \c device_start is called
/// for the device. This member will only be called once, it will
/// not be called multiple times if device starting is postponed.
///
/// This member must not throw #device_missing_dependencies (start
/// order dependencies should be checked in #interface_pre_start).
/// This is the appropriate place to allocate resources like
/// timers and register for save states.
/// \sa interface_pre_start device_t::device_start
/// device_t::save_item device_t::save_pointer
virtual void interface_post_start() ATTR_COLD;
/// \brief Mix-in reset handler
///
/// Implement this member to provide reset behaviour. This is
/// called before device_reset is called for the device, and before
/// any child devices are reset. Only implement warm reset
/// called before \c device_reset is called for the device, and
/// before any child devices are reset. Only implement warm reset
/// behaviour in this member. Initial cold reset conditions should
/// be set up in #interface_pre_start and/or #interface_post_start.
/// If you need to provide additional behaviour after child devices
@ -1046,7 +1137,7 @@ public:
///
/// Implement this member to provide additional reset behaviour
/// after child devices are reset. This is called after
/// device_reset_after_children has been called for the device.
/// \c device_reset_after_children has been called for the device.
/// \sa interface_pre_reset device_t::device_reset
/// device_t::device_reset_after_children
virtual void interface_post_reset() ATTR_COLD;
@ -1056,7 +1147,7 @@ public:
/// Implement this member to perform additional tasks on ending an
/// emulation session. Do not deallocate anything that may need to
/// be referenced from another device_interface mix-in's
/// interface_pre_stop member or from the device's device_stop
/// \c interface_pre_stop member or from the device's \c device_stop
/// member. This is called before device_stop is called for the
/// device.
/// \sa interface_post_stop device_t::device_stop
@ -1066,7 +1157,7 @@ public:
///
/// Implement this member to perform additional tasks on ending an
/// emulation session after the device is stopped. You can
/// deallocate memory here. This is called after device_stop is
/// deallocate memory here. This is called after \c device_stop is
/// called for the device.
/// \sa interface_pre_stop device_t::device_stop
virtual void interface_post_stop() ATTR_COLD;
@ -1077,7 +1168,7 @@ public:
/// registered save state items are recorded. For example it may be
/// necessary to flush caches, serialise self-referencing members or
/// pointers into data structures. This is called before
/// device_pre_save is called for the device.
/// \c device_pre_save is called for the device.
/// \sa interface_post_load device_t::device_pre_save
virtual void interface_pre_save() ATTR_COLD;
@ -1087,7 +1178,7 @@ public:
/// registered save state items are loaded. For example it may be
/// necessary to update or invalidate caches, or de-serialise
/// pointers into data structures. This is called before
/// device_post_load is called for the device.
/// \c device_post_load is called for the device.
/// \sa interface_pre_save device_t::device_post_load
virtual void interface_post_load() ATTR_COLD;

View File

@ -18,6 +18,7 @@
#define MAME_EMU_EMUMEM_H
#include <optional>
#include <set>
#include <type_traits>
using s8 = std::int8_t;
@ -476,7 +477,6 @@ template<int Width, int AddrShift> class memory_units_descriptor;
// =====================-> The root class of all handlers
// Handlers the refcounting as part of the interface
#include <set>
class handler_entry
{
DISABLE_COPYING(handler_entry);

View File

@ -392,7 +392,7 @@ extern game_driver const GAME_NAME(NAME) \
/// avoid repetition. Screen orientation flags may be included here.
/// \sa GAME GAMEL COMP SYST
#define CONS(YEAR, NAME, PARENT, COMPAT, MACHINE, INPUT, CLASS, INIT, COMPANY, FULLNAME, FLAGS) \
GAME_DRIVER_TRAITS(NAME,FULLNAME) \
GAME_DRIVER_TRAITS(NAME, FULLNAME) \
extern game_driver const GAME_NAME(NAME) \
{ \
GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \
@ -457,8 +457,8 @@ extern game_driver const GAME_NAME(NAME) \
/// all systems implemented using the class in the class itself to
/// avoid repetition. Screen orientation flags may be included here.
/// \sa GAME GAMEL CONS SYST
#define COMP(YEAR,NAME,PARENT,COMPAT,MACHINE,INPUT,CLASS,INIT,COMPANY,FULLNAME,FLAGS) \
GAME_DRIVER_TRAITS(NAME,FULLNAME) \
#define COMP(YEAR, NAME, PARENT, COMPAT, MACHINE, INPUT, CLASS, INIT, COMPANY, FULLNAME, FLAGS) \
GAME_DRIVER_TRAITS(NAME, FULLNAME) \
extern game_driver const GAME_NAME(NAME) \
{ \
GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \
@ -523,8 +523,8 @@ extern game_driver const GAME_NAME(NAME) \
/// all systems implemented using the class in the class itself to
/// avoid repetition. Screen orientation flags may be included here.
/// \sa GAME GAMEL CONS COMP
#define SYST(YEAR,NAME,PARENT,COMPAT,MACHINE,INPUT,CLASS,INIT,COMPANY,FULLNAME,FLAGS) \
GAME_DRIVER_TRAITS(NAME,FULLNAME) \
#define SYST(YEAR, NAME, PARENT, COMPAT, MACHINE, INPUT, CLASS, INIT, COMPANY, FULLNAME, FLAGS) \
GAME_DRIVER_TRAITS(NAME, FULLNAME) \
extern game_driver const GAME_NAME(NAME) \
{ \
GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \

View File

@ -51,18 +51,36 @@ enum save_error
typedef named_delegate<void ()> save_prepost_delegate;
// use this to declare a given type is a simple, non-pointer type that can be
// saved; in general, this is intended only to be used for specific enum types
// defined by your device
/// \brief Declare a type as safe to automatically save/restore
///
/// Use this to declare that a given type is a simple, non-pointer type
/// that can be saved and restored. In general, this should only be
/// be used for specific enum types that have fixed width integer types
/// as their storage classes.
/// \param TYPE The name of the type to declare safe to save.
/// \sa ALLOW_SAVE_TYPE_AND_VECTOR
#define ALLOW_SAVE_TYPE(TYPE) \
template <> struct save_manager::is_atom<TYPE> : public std::true_type { };
// use this as above, but also to declare that std::vector<TYPE> is safe as well
/// \brief Declare a type as safe to automatically save/restore,
/// including in \c std::vector instances
///
/// Used the same way as #ALLOW_SAVE_TYPE, but also declares that
/// \c std::vector instances containing the type are safe to save.
/// that can be saved and restored. This must not be used if
/// \c std::vector is specialised in an incompatible way.
/// \param TYPE The name of the type to declare safe to save.
/// \sa ALLOW_SAVE_TYPE
#define ALLOW_SAVE_TYPE_AND_VECTOR(TYPE) \
ALLOW_SAVE_TYPE(TYPE) \
template <> struct save_manager::is_vector_safe<TYPE> : public std::true_type { };
// use this for saving members of structures in arrays
/// \brief Helper for referring to members of structures in arrays
///
/// Expands to the necessary reference, pointer to member and name to
/// refer to a structure member.
/// \param s Reference to a C array or \c std::array of structures.
/// \param m Name of the structure member to refer to.
#define STRUCT_MEMBER(s, m) s, &save_manager::pointer_unwrap<decltype(s)>::underlying_type::m, #s "." #m

View File

@ -2,17 +2,28 @@
// copyright-holders:Aaron Giles
/***************************************************************************
delegate.c
delegate.cpp
Templates and classes to enable delegates for callbacks.
***************************************************************************/
#include <cassert>
#include <cstdint>
#include <cstdio>
#include "delegate.h"
#include <cstdio>
//**************************************************************************
// MACROS
//**************************************************************************
#if defined(LOG_DELEGATES)
#define LOG(...) printf(__VA_ARGS__)
#else
#define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false)
#endif
//**************************************************************************
// GLOBAL VARIABLES
@ -20,7 +31,7 @@
#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE)
delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { {0 }};
delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { { 0 } };
#endif
@ -40,10 +51,9 @@ delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { {0 }};
delegate_generic_function delegate_mfp::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 defined(LOG_DELEGATES)
printf("Calculated Addr = %08x\n", (uintptr_t)(void*)(m_function));
#endif
if ((m_this_delta & 1) == 0)
{
LOG("Calculated Addr = %p\n", reinterpret_cast<void const *>(uintptr_t(m_function)));
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
return reinterpret_cast<delegate_generic_function>(m_function);
}
@ -51,27 +61,22 @@ delegate_generic_function delegate_mfp::convert_to_generic(delegate_generic_clas
// otherwise, it is the byte index into the vtable where the actual function lives
std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
#if defined(LOG_DELEGATES)
printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1)));
#endif
LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast<void const *>(uintptr_t(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1))));
return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1);
#else
// apply the "this" delta to the object first
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(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)) {
#if defined(LOG_DELEGATES)
printf("Calculated Addr = %08x\n", (uintptr_t)(void*)(m_function));
#endif
if (!(m_function & 1))
{
LOG("Calculated Addr = %p\n", reinterpret_cast<void const *>(uintptr_t(m_function)));
return reinterpret_cast<delegate_generic_function>(m_function);
}
// otherwise, it is the byte index into the vtable where the actual function lives
std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
#if defined(LOG_DELEGATES)
printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1)));
#endif
LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast<void *>(uintptr_t(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1))));
return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1);
#endif
}

View File

@ -77,9 +77,12 @@
#pragma once
// standard C++ includes
#include <any>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <functional>
#include <exception>
#include <type_traits>
#include <typeinfo>
#include <utility>
@ -571,9 +574,54 @@ class delegate<ReturnType (Params...)> : public delegate_base<ReturnType, Params
{
private:
using basetype = delegate_base<ReturnType, Params...>;
using functional_type = std::function<ReturnType (Params...)>;
using functoid_setter = void (*)(delegate &);
functional_type m_std_func;
template <typename T> static constexpr bool matching_non_const_call(T &&) { return false; }
template <typename T> static constexpr bool matching_non_const_call(ReturnType (T::*)(Params...)) { return true; }
template <typename T> static constexpr bool matching_const_call(T &&) { return false; }
template <typename T> static constexpr bool matching_const_call(ReturnType (T::*)(Params...) const) { return true; }
template <typename T>
static functoid_setter make_functoid_setter()
{
using unwrapped_type = std::remove_cv_t<std::remove_reference_t<T> >;
if constexpr (matching_non_const_call(&T::operator()))
{
return
[] (delegate &obj)
{
obj.basetype::operator=(
basetype(
static_cast<ReturnType (unwrapped_type::*)(Params...)>(&unwrapped_type::operator()),
std::any_cast<unwrapped_type>(&obj.m_functoid)));
};
}
else if constexpr (matching_const_call(&T::operator()))
{
return
[] (delegate &obj)
{
obj.basetype::operator=(
basetype(
static_cast<ReturnType (unwrapped_type::*)(Params...) const>(&unwrapped_type::operator()),
std::any_cast<unwrapped_type>(&obj.m_functoid)));
};
}
else
{
return
[] (delegate &obj)
{
obj.basetype::operator=(
basetype(
[] (unwrapped_type &f, Params... args) { return ReturnType(f(std::forward<Params>(args)...)); },
std::any_cast<unwrapped_type>(&obj.m_functoid)));
};
}
}
std::any m_functoid;
functoid_setter m_set_functoid = nullptr;
protected:
template <class FunctionClass> using traits = typename basetype::template traits<FunctionClass>;
@ -581,35 +629,85 @@ protected:
template <class FunctionClass> using const_member_func_type = typename traits<FunctionClass>::const_member_func_type;
template <class FunctionClass> using static_ref_func_type = typename traits<FunctionClass>::static_ref_func_type;
template <typename T> using suitable_functoid = std::is_convertible<std::invoke_result_t<T, Params...>, ReturnType>;
public:
// create a standard set of constructors
delegate() : basetype() { }
delegate(delegate const &src) : basetype(src.m_std_func ? basetype(&functional_type::operator(), &m_std_func) : static_cast<basetype const &>(src)), m_std_func(src.m_std_func) { }
delegate(delegate &&src) : basetype(src.m_std_func ? basetype(&functional_type::operator(), &m_std_func) : std::move(static_cast<basetype &>(src))), m_std_func(std::move(src.m_std_func)) { }
delegate(delegate const &src, delegate_late_bind &object) : basetype(src.m_std_func ? basetype(&functional_type::operator(), &m_std_func) : basetype(src, object)), m_std_func(src.m_std_func) { }
delegate(delegate const &src)
: basetype(src.m_functoid.has_value() ? static_cast<const basetype &>(basetype()) : src)
, m_functoid(src.m_functoid)
, m_set_functoid(src.m_set_functoid)
{
if (m_functoid.has_value())
m_set_functoid(*this);
}
delegate(delegate &&src)
: basetype(src.m_functoid.has_value() ? basetype() : std::move(src))
, m_functoid(std::move(src.m_functoid))
, m_set_functoid(std::move(src.m_set_functoid))
{
if (m_functoid.has_value())
m_set_functoid(*this);
}
delegate(delegate const &src, delegate_late_bind &object)
: basetype(src.m_functoid.has_value() ? basetype() : basetype(src, object))
, m_functoid(src.m_functoid)
, m_set_functoid(src.m_set_functoid)
{
if (m_functoid.has_value())
m_set_functoid(*this);
}
template <class FunctionClass> delegate(member_func_type<FunctionClass> funcptr, FunctionClass *object) : basetype(funcptr, object) { }
template <class FunctionClass> delegate(const_member_func_type<FunctionClass> funcptr, FunctionClass *object) : basetype(funcptr, object) { }
explicit delegate(functional_type &&functoid) : basetype(&functional_type::operator(), &m_std_func), m_std_func(std::move(functoid)) { }
template <class FunctionClass> delegate(static_ref_func_type<FunctionClass> funcptr, FunctionClass *object) : basetype(funcptr, object) { }
template <typename T>
explicit delegate(T &&functoid, std::enable_if_t<suitable_functoid<T>::value, int> = 0)
: basetype()
, m_functoid(std::forward<T>(functoid))
, m_set_functoid(make_functoid_setter<T>())
{
m_set_functoid(*this);
}
delegate &operator=(std::nullptr_t) noexcept
{
reset();
return *this;
}
delegate &operator=(delegate const &src)
{
if (src.m_std_func)
basetype::operator=(basetype(&functional_type::operator(), &m_std_func));
m_functoid = src.m_functoid;
m_set_functoid = src.m_set_functoid;
if (m_functoid.has_value())
m_set_functoid(*this);
else
basetype::operator=(src);
m_std_func = src.m_std_func;
return *this;
}
delegate &operator=(delegate &&src)
{
if (src.m_std_func)
basetype::operator=(basetype(&functional_type::operator(), &m_std_func));
m_functoid = std::move(src.m_functoid);
m_set_functoid = std::move(src.m_set_functoid);
if (m_functoid.has_value())
m_set_functoid(*this);
else
basetype::operator=(std::move(src));
m_std_func = std::move(src.m_std_func);
return *this;
}
void reset() noexcept
{
basetype::operator=(basetype());
m_functoid.reset();
m_set_functoid = nullptr;
}
};
#endif // MAME_UTIL_DELEGATE_H