From d334586156dabf0db220b843c81f2a54cc3a2ece Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Thu, 16 Sep 2021 00:28:09 +1000 Subject: [PATCH] -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. --- scripts/target/mame/tiny.lua | 1 - src/emu/devdelegate.h | 21 +++- src/emu/device.h | 183 ++++++++++++++++++++++++++--------- src/emu/emumem.h | 2 +- src/emu/gamedrv.h | 10 +- src/emu/save.h | 28 +++++- src/lib/util/delegate.cpp | 43 ++++---- src/lib/util/delegate.h | 128 +++++++++++++++++++++--- 8 files changed, 320 insertions(+), 96 deletions(-) diff --git a/scripts/target/mame/tiny.lua b/scripts/target/mame/tiny.lua index 87d5aa546ee..b8a977d6acc 100644 --- a/scripts/target/mame/tiny.lua +++ b/scripts/target/mame/tiny.lua @@ -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", diff --git a/src/emu/devdelegate.h b/src/emu/devdelegate.h index 906e1da9274..63bfdb5c369 100644 --- a/src/emu/devdelegate.h +++ b/src/emu/devdelegate.h @@ -83,6 +83,8 @@ protected: template using const_member_func_type = typename basetype::template const_member_func_type; template using static_ref_func_type = typename basetype::template static_ref_func_type; + template using suitable_functoid = typename basetype::template suitable_functoid; + public: // create a standard set of constructors named_delegate() = default; @@ -91,12 +93,18 @@ public: template named_delegate(member_func_type funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } template named_delegate(const_member_func_type funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } template named_delegate(static_ref_func_type funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } - template named_delegate(T &&funcptr, std::enable_if_t, T>::value, char const *> name) : basetype(std::forward(funcptr)), m_name(name) { } + template named_delegate(T &&funcptr, std::enable_if_t::value, char const *> name) : basetype(std::forward(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 : protected named_delegate; + template + using suitable_functoid = typename basetype::template suitable_functoid; + template using is_related_device_implementation = std::bool_constant && std::is_base_of_v >; template @@ -189,7 +200,7 @@ public: // construct with callable object template - device_delegate(device_t &owner, T &&funcptr, std::enable_if_t, T>::value, char const *> name) + device_delegate(device_t &owner, T &&funcptr, std::enable_if_t::value, char const *> name) : basetype(std::forward(funcptr), name) , detail::device_delegate_helper(owner) { basetype::operator=(basetype(std::forward(funcptr), name)); } @@ -219,12 +230,14 @@ public: { basetype::operator=(basetype(funcptr, name, static_cast(&object))); set_tag(finder_target().first); } // setter that takes a functoid - template std::enable_if_t, T>::value> set(T &&funcptr, char const *name) + template std::enable_if_t::value> set(T &&funcptr, char const *name) { basetype::operator=(basetype(std::forward(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()); } diff --git a/src/emu/device.h b/src/emu/device.h index cab07888d35..77c12cb2d56 100644 --- a/src/emu/device.h +++ b/src/emu/device.h @@ -353,7 +353,7 @@ constexpr auto driver_device_creator = &emu::detail::driver_tag_func 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 + /// \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 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 + + /// \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 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 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(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 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; diff --git a/src/emu/emumem.h b/src/emu/emumem.h index 31687ba2e4e..7b156bec8a2 100644 --- a/src/emu/emumem.h +++ b/src/emu/emumem.h @@ -18,6 +18,7 @@ #define MAME_EMU_EMUMEM_H #include +#include #include using s8 = std::int8_t; @@ -476,7 +477,6 @@ template class memory_units_descriptor; // =====================-> The root class of all handlers // Handlers the refcounting as part of the interface -#include class handler_entry { DISABLE_COPYING(handler_entry); diff --git a/src/emu/gamedrv.h b/src/emu/gamedrv.h index 38f46a40bd2..a1cb8a40155 100644 --- a/src/emu/gamedrv.h +++ b/src/emu/gamedrv.h @@ -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), \ diff --git a/src/emu/save.h b/src/emu/save.h index cfd674e738e..2ea17ab9921 100644 --- a/src/emu/save.h +++ b/src/emu/save.h @@ -51,18 +51,36 @@ enum save_error typedef named_delegate 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 : public std::true_type { }; -// use this as above, but also to declare that std::vector 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 : 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::underlying_type::m, #s "." #m diff --git a/src/lib/util/delegate.cpp b/src/lib/util/delegate.cpp index 1ba81916030..8c816928625 100644 --- a/src/lib/util/delegate.cpp +++ b/src/lib/util/delegate.cpp @@ -2,17 +2,28 @@ // copyright-holders:Aaron Giles /*************************************************************************** - delegate.c + delegate.cpp Templates and classes to enable delegates for callbacks. ***************************************************************************/ -#include -#include -#include #include "delegate.h" +#include + + +//************************************************************************** +// 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(uintptr_t(m_function))); object = reinterpret_cast(reinterpret_cast(object) + m_this_delta); return reinterpret_cast(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(object); -#if defined(LOG_DELEGATES) - printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast(vtable_base + m_function + m_this_delta - 1))); -#endif + LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast(uintptr_t(*reinterpret_cast(vtable_base + m_function + m_this_delta - 1)))); return *reinterpret_cast(vtable_base + m_function + m_this_delta - 1); #else // apply the "this" delta to the object first object = reinterpret_cast(reinterpret_cast(object) + m_this_delta); // if the low bit of the vtable index is clear, then it is just a raw function pointer - if (!(m_function & 1)) { -#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(uintptr_t(m_function))); return reinterpret_cast(m_function); } // otherwise, it is the byte index into the vtable where the actual function lives std::uint8_t *vtable_base = *reinterpret_cast(object); -#if defined(LOG_DELEGATES) - printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast(vtable_base + m_function - 1))); -#endif + LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast(uintptr_t(*reinterpret_cast(vtable_base + m_function - 1)))); return *reinterpret_cast(vtable_base + m_function - 1); #endif } diff --git a/src/lib/util/delegate.h b/src/lib/util/delegate.h index 454045ba98e..169a48e2195 100644 --- a/src/lib/util/delegate.h +++ b/src/lib/util/delegate.h @@ -77,9 +77,12 @@ #pragma once -// standard C++ includes +#include +#include +#include #include -#include +#include +#include #include #include @@ -571,9 +574,54 @@ class delegate : public delegate_base; - using functional_type = std::function; + using functoid_setter = void (*)(delegate &); - functional_type m_std_func; + template static constexpr bool matching_non_const_call(T &&) { return false; } + template static constexpr bool matching_non_const_call(ReturnType (T::*)(Params...)) { return true; } + template static constexpr bool matching_const_call(T &&) { return false; } + template static constexpr bool matching_const_call(ReturnType (T::*)(Params...) const) { return true; } + + template + static functoid_setter make_functoid_setter() + { + using unwrapped_type = std::remove_cv_t >; + if constexpr (matching_non_const_call(&T::operator())) + { + return + [] (delegate &obj) + { + obj.basetype::operator=( + basetype( + static_cast(&unwrapped_type::operator()), + std::any_cast(&obj.m_functoid))); + }; + } + else if constexpr (matching_const_call(&T::operator())) + { + return + [] (delegate &obj) + { + obj.basetype::operator=( + basetype( + static_cast(&unwrapped_type::operator()), + std::any_cast(&obj.m_functoid))); + }; + } + else + { + return + [] (delegate &obj) + { + obj.basetype::operator=( + basetype( + [] (unwrapped_type &f, Params... args) { return ReturnType(f(std::forward(args)...)); }, + std::any_cast(&obj.m_functoid))); + }; + } + } + + std::any m_functoid; + functoid_setter m_set_functoid = nullptr; protected: template using traits = typename basetype::template traits; @@ -581,35 +629,85 @@ protected: template using const_member_func_type = typename traits::const_member_func_type; template using static_ref_func_type = typename traits::static_ref_func_type; + template using suitable_functoid = std::is_convertible, 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(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(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(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 delegate(member_func_type funcptr, FunctionClass *object) : basetype(funcptr, object) { } template delegate(const_member_func_type 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 delegate(static_ref_func_type funcptr, FunctionClass *object) : basetype(funcptr, object) { } + template + explicit delegate(T &&functoid, std::enable_if_t::value, int> = 0) + : basetype() + , m_functoid(std::forward(functoid)) + , m_set_functoid(make_functoid_setter()) + { + 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