-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/includes/exidy.h",
MAME_DIR .. "src/mame/audio/exidy.cpp", MAME_DIR .. "src/mame/audio/exidy.cpp",
MAME_DIR .. "src/mame/audio/exidy.h", 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.cpp",
MAME_DIR .. "src/mame/audio/exidy440.h", MAME_DIR .. "src/mame/audio/exidy440.h",
MAME_DIR .. "src/mame/audio/nl_fireone.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 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 <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: public:
// create a standard set of constructors // create a standard set of constructors
named_delegate() = default; 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(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(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 <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 // allow assignment
named_delegate &operator=(named_delegate const &src) = default; 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; } char const *name() const { return m_name; }
// unsetter
void reset() noexcept { basetype::reset(); m_name = nullptr; }
}; };
// ======================> device_delegate // ======================> device_delegate
@ -111,6 +119,9 @@ class device_delegate<ReturnType (Params...)> : protected named_delegate<ReturnT
private: private:
using basetype = named_delegate<ReturnType (Params...)>; using basetype = named_delegate<ReturnType (Params...)>;
template <typename T>
using suitable_functoid = typename basetype::template suitable_functoid<T>;
template <class T, class U> 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> >; 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> template <class T, class U>
@ -189,7 +200,7 @@ public:
// construct with callable object // construct with callable object
template <typename T> 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) : basetype(std::forward<T>(funcptr), name)
, detail::device_delegate_helper(owner) , detail::device_delegate_helper(owner)
{ basetype::operator=(basetype(std::forward<T>(funcptr), name)); } { 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); } { basetype::operator=(basetype(funcptr, name, static_cast<D *>(&object))); set_tag(finder_target().first); }
// setter that takes a functoid // 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)); } { basetype::operator=(basetype(std::forward<T>(funcptr), name)); }
// unsetter // unsetter
void set(std::nullptr_t) 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 // perform the binding
void resolve() { if (!basetype::isnull() && !basetype::has_object()) basetype::late_bind(bound_object()); } 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. /// letters and underscores.
/// \param Class The exposed device class name. Must be the device /// \param Class The exposed device class name. Must be the device
/// implementation class, or a public base of it, and must be derived /// 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 /// \sa DECLARE_DEVICE_TYPE_NS DEFINE_DEVICE_TYPE
/// DEFINE_DEVICE_TYPE_PRIVATE /// DEFINE_DEVICE_TYPE_PRIVATE
#define DECLARE_DEVICE_TYPE(Type, Class) \ #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. /// containing the exposed device class.
/// \param Class The exposed device class name, without namespace /// \param Class The exposed device class name, without namespace
/// qualifiers. Must be the device implementation class, or a public /// 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 /// \sa DECLARE_DEVICE_TYPE DEFINE_DEVICE_TYPE
/// DEFINE_DEVICE_TYPE_PRIVATE /// DEFINE_DEVICE_TYPE_PRIVATE
#define DECLARE_DEVICE_TYPE_NS(Type, Namespace, Class) \ #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. /// letters and underscores.
/// \param Base The fully-qualified exposed device class name. Must be /// \param Base The fully-qualified exposed device class name. Must be
/// a public base of the device implementation class, and 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. /// \param Class The fully-qualified device implementation class name.
/// Must be derived from the exposed device class, and indirectly from /// Must be derived from the exposed device class, and indirectly from
/// #device_t. /// #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 { }; class device_missing_dependencies : public emu_exception { };
// timer IDs for devices // timer IDs for devices
typedef u32 device_timer_id; 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 class device_t : public delegate_late_bind
{ {
DISABLE_COPYING(device_t); 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 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); } void timer_expired(emu_timer &timer, device_timer_id id, int param, void *ptr) { device_timer(timer, id, param, ptr); }
// state saving interfaces /// \brief Register data for save states
template<typename ItemType> ///
/// 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) void ATTR_COLD save_item(ItemType &value, const char *valname, int index = 0)
{ {
assert(m_save); assert(m_save);
m_save->save_item(this, name(), tag(), index, value, valname); 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) void ATTR_COLD save_item(ItemType &value, ElementType StructType::*element, const char *valname, int index = 0)
{ {
assert(m_save); assert(m_save);
m_save->save_item(this, name(), tag(), index, value, element, valname); 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> template<typename ItemType>
void ATTR_COLD save_pointer(ItemType &&value, const char *valname, u32 count, int index = 0) void ATTR_COLD save_pointer(ItemType &&value, const char *valname, u32 count, int index = 0)
{ {
assert(m_save); assert(m_save);
m_save->save_pointer(this, name(), tag(), index, std::forward<ItemType>(value), valname, count); 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> 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) 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 /// Perform any final configuration tasks after all devices in the
/// system have added machine configuration. This is called after /// system have added machine configuration. This is called after
/// any #device_interface mix-in interface_config_complete members /// any #device_interface mix-in \c interface_config_complete
/// have completed. /// members have completed.
/// ///
/// Note that automatic object finders will not have been resolved /// Note that automatic object finders will not have been resolved
/// at the time this member is called. /// at the time this member is called.
@ -793,29 +879,29 @@ protected:
/// Implement this member to complete object resolution before any /// Implement this member to complete object resolution before any
/// devices are started. For example it may be necessary to resolve /// devices are started. For example it may be necessary to resolve
/// callbacks before any devices start so initial input conditions /// callbacks before any devices start so initial input conditions
/// can be set. This is called after all registerd automatic object /// can be set. This is called after all registered automatic
/// finders are resolved. /// object finders are resolved.
virtual void device_resolve_objects() ATTR_COLD; virtual void device_resolve_objects() ATTR_COLD;
/// \brief Device start handler /// \brief Device start handler
/// ///
/// Implement this member to set up the initial state of the device /// Implement this member to set up the initial state of the device
/// on start. This will be called after all #device_interface /// on start. This will be called after all #device_interface
// /mix-in interface_pre_start members have completed successfully. /// mix-in \c interface_pre_start members have completed
/// If the device can't start until another device has completed /// successfully. If the device can't start until another device
/// starting, throw a #device_missing_dependencies exception. /// has completed starting, throw a #device_missing_dependencies
/// Starting will be postponed until additional devices have been /// exception. Starting will be postponed until additional devices
/// started. /// have started.
/// ///
/// If a device's base class is not device_t, it's good practice to /// If a device's direct base class is not #device_t, it's good
/// check start order dependencies (and throw /// practice to check start order dependencies (and throw
/// #device_missing_dependencies if necessary) before calling the /// #device_missing_dependencies if necessary) before calling the
/// base implementation. This will ensure that the base /// base implementation. This will ensure that the base
/// implementation won't be called twice if starting needs to be /// implementation won't be called twice if starting needs to be
/// postponed. /// postponed.
/// ///
/// This is the correct place to register for save states. /// 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_pre_start
/// device_interface::interface_post_start /// device_interface::interface_post_start
virtual void device_start() ATTR_COLD = 0; virtual void device_start() ATTR_COLD = 0;
@ -824,8 +910,8 @@ protected:
/// ///
/// Implement this member to perform additional tasks on ending an /// Implement this member to perform additional tasks on ending an
/// emulation session. You may deallocate memory here. This is /// emulation session. You may deallocate memory here. This is
/// called after interface_pre_stop is called for all /// called after \c interface_pre_stop is called for all
/// #device_interface mix-ins, and before interface_post_stop is /// #device_interface mix-ins, and before \c interface_post_stop is
/// called for any #device_interface mix-ins. /// called for any #device_interface mix-ins.
/// \sa device_interface::interface_pre_stop /// \sa device_interface::interface_pre_stop
/// device_interface::interface_post_stop /// device_interface::interface_post_stop
@ -834,7 +920,7 @@ protected:
/// \brief Device reset handler /// \brief Device reset handler
/// ///
/// Implement this member to provide reset behaviour. This is /// 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. /// members have completed, and before any child devices are reset.
/// All devices are reset at the beginning of an emulation session /// All devices are reset at the beginning of an emulation session
/// (after all devices have been started), and also when the user /// (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 /// after child devices are reset. This is called when resetting a
/// device after #device_reset has been called and all child devices /// device after #device_reset has been called and all child devices
/// have been reset, and before any #device_interface mix-in /// 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 /// \sa device_reset device_interface::interface_pre_reset
/// device_interface::interface_post_reset /// device_interface::interface_post_reset
virtual void device_reset_after_children() ATTR_COLD; virtual void device_reset_after_children() ATTR_COLD;
@ -870,8 +956,10 @@ protected:
/// registered save state items are recorded. For example it may be /// registered save state items are recorded. For example it may be
/// necessary to flush caches, serialise self-referencing members or /// necessary to flush caches, serialise self-referencing members or
/// pointers into data structures. This is called after all /// pointers into data structures. This is called after all
/// #device_interface mix-in interface_pre_save members are called. /// #device_interface mix-in \c interface_pre_save members are
/// \sa device_post_load device_interface::interface_pre_save /// called.
/// \sa device_post_load save_item save_pointer
/// device_interface::interface_pre_save
virtual void device_pre_save() ATTR_COLD; virtual void device_pre_save() ATTR_COLD;
/// \brief Complete save state loading /// \brief Complete save state loading
@ -880,8 +968,10 @@ protected:
/// registered save state items are loaded. For example it may be /// registered save state items are loaded. For example it may be
/// necessary to update or invalidate caches, or de-serialise /// necessary to update or invalidate caches, or de-serialise
/// pointers into data structures. This is called after all /// pointers into data structures. This is called after all
/// #device_interface mix-in interface_post_load members are called. /// #device_interface mix-in \c interface_post_load members are
/// \sa device_pre_save device_interface::interface_post_load /// called.
/// \sa device_pre_save save_item save_pointer
/// device_interface::interface_post_load
virtual void device_post_load() ATTR_COLD; virtual void device_post_load() ATTR_COLD;
virtual void device_clock_changed(); virtual void device_clock_changed();
@ -968,7 +1058,7 @@ public:
/// ///
/// Perform any final configuration tasks after all devices in the /// Perform any final configuration tasks after all devices in the
/// system have added machine configuration. This is called before /// 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 /// Note that automatic object finders will not have been resolved
/// at this time. /// at this time.
@ -1003,12 +1093,12 @@ public:
/// have been started. /// have been started.
/// ///
/// Note that this member may be called multiple times if another /// Note that this member may be called multiple times if another
/// device_interface mix-in throws a #device_missing_dependencies /// \c device_interface mix-in throws a #device_missing_dependencies
/// exception from its interface_pre_start member, or if the device /// exception from its \c interface_pre_start member, or if the
/// throws a #device_missing_dependencies exception from its /// device throws a #device_missing_dependencies exception from its
/// device_start member. You must check to ensure that operations /// \c device_start member. You must check to ensure that
/// like resource allocation are not performed multiple times, or /// operations like resource allocation are not performed multiple
/// postpone them until #interface_post_start is called. /// times, or postpone them until #interface_post_start is called.
/// ///
/// It's simpler to register for save states when /// It's simpler to register for save states when
/// #interface_post_start is called. /// #interface_post_start is called.
@ -1019,22 +1109,23 @@ public:
/// ///
/// Implement this member to complete mix-in start-up. This is /// Implement this member to complete mix-in start-up. This is
/// called after #interface_pre_start is called for all /// called after #interface_pre_start is called for all
/// device_interface mix-ins, and after device_start is called for /// device_interface mix-ins, and after \c device_start is called
/// the device. This member will only be called once, it will not /// for the device. This member will only be called once, it will
/// be called multiple times if device starting is postponed. /// not be called multiple times if device starting is postponed.
/// ///
/// This member must not throw #device_missing_dependencies (start /// This member must not throw #device_missing_dependencies (start
/// order dependencies should be checked in #interface_pre_start). /// order dependencies should be checked in #interface_pre_start).
/// This is the appropriate place to allocate resources like /// This is the appropriate place to allocate resources like
/// timers and register for save states. /// timers and register for save states.
/// \sa interface_pre_start device_t::device_start /// \sa interface_pre_start device_t::device_start
/// device_t::save_item device_t::save_pointer
virtual void interface_post_start() ATTR_COLD; virtual void interface_post_start() ATTR_COLD;
/// \brief Mix-in reset handler /// \brief Mix-in reset handler
/// ///
/// Implement this member to provide reset behaviour. This is /// Implement this member to provide reset behaviour. This is
/// called before device_reset is called for the device, and before /// called before \c device_reset is called for the device, and
/// any child devices are reset. Only implement warm reset /// before any child devices are reset. Only implement warm reset
/// behaviour in this member. Initial cold reset conditions should /// behaviour in this member. Initial cold reset conditions should
/// be set up in #interface_pre_start and/or #interface_post_start. /// be set up in #interface_pre_start and/or #interface_post_start.
/// If you need to provide additional behaviour after child devices /// If you need to provide additional behaviour after child devices
@ -1046,7 +1137,7 @@ public:
/// ///
/// Implement this member to provide additional reset behaviour /// Implement this member to provide additional reset behaviour
/// after child devices are reset. This is called after /// 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 /// \sa interface_pre_reset device_t::device_reset
/// device_t::device_reset_after_children /// device_t::device_reset_after_children
virtual void interface_post_reset() ATTR_COLD; virtual void interface_post_reset() ATTR_COLD;
@ -1056,7 +1147,7 @@ public:
/// Implement this member to perform additional tasks on ending an /// Implement this member to perform additional tasks on ending an
/// emulation session. Do not deallocate anything that may need to /// emulation session. Do not deallocate anything that may need to
/// be referenced from another device_interface mix-in's /// 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 /// member. This is called before device_stop is called for the
/// device. /// device.
/// \sa interface_post_stop device_t::device_stop /// \sa interface_post_stop device_t::device_stop
@ -1066,7 +1157,7 @@ public:
/// ///
/// Implement this member to perform additional tasks on ending an /// Implement this member to perform additional tasks on ending an
/// emulation session after the device is stopped. You can /// 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. /// called for the device.
/// \sa interface_pre_stop device_t::device_stop /// \sa interface_pre_stop device_t::device_stop
virtual void interface_post_stop() ATTR_COLD; virtual void interface_post_stop() ATTR_COLD;
@ -1077,7 +1168,7 @@ public:
/// registered save state items are recorded. For example it may be /// registered save state items are recorded. For example it may be
/// necessary to flush caches, serialise self-referencing members or /// necessary to flush caches, serialise self-referencing members or
/// pointers into data structures. This is called before /// 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 /// \sa interface_post_load device_t::device_pre_save
virtual void interface_pre_save() ATTR_COLD; virtual void interface_pre_save() ATTR_COLD;
@ -1087,7 +1178,7 @@ public:
/// registered save state items are loaded. For example it may be /// registered save state items are loaded. For example it may be
/// necessary to update or invalidate caches, or de-serialise /// necessary to update or invalidate caches, or de-serialise
/// pointers into data structures. This is called before /// 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 /// \sa interface_pre_save device_t::device_post_load
virtual void interface_post_load() ATTR_COLD; virtual void interface_post_load() ATTR_COLD;

View File

@ -18,6 +18,7 @@
#define MAME_EMU_EMUMEM_H #define MAME_EMU_EMUMEM_H
#include <optional> #include <optional>
#include <set>
#include <type_traits> #include <type_traits>
using s8 = std::int8_t; using s8 = std::int8_t;
@ -476,7 +477,6 @@ template<int Width, int AddrShift> class memory_units_descriptor;
// =====================-> The root class of all handlers // =====================-> The root class of all handlers
// Handlers the refcounting as part of the interface // Handlers the refcounting as part of the interface
#include <set>
class handler_entry class handler_entry
{ {
DISABLE_COPYING(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. /// avoid repetition. Screen orientation flags may be included here.
/// \sa GAME GAMEL COMP SYST /// \sa GAME GAMEL COMP SYST
#define CONS(YEAR, NAME, PARENT, COMPAT, MACHINE, INPUT, CLASS, INIT, COMPANY, FULLNAME, FLAGS) \ #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) \ extern game_driver const GAME_NAME(NAME) \
{ \ { \
GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \ 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 /// all systems implemented using the class in the class itself to
/// avoid repetition. Screen orientation flags may be included here. /// avoid repetition. Screen orientation flags may be included here.
/// \sa GAME GAMEL CONS SYST /// \sa GAME GAMEL CONS SYST
#define COMP(YEAR,NAME,PARENT,COMPAT,MACHINE,INPUT,CLASS,INIT,COMPANY,FULLNAME,FLAGS) \ #define COMP(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) \ extern game_driver const GAME_NAME(NAME) \
{ \ { \
GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \ 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 /// all systems implemented using the class in the class itself to
/// avoid repetition. Screen orientation flags may be included here. /// avoid repetition. Screen orientation flags may be included here.
/// \sa GAME GAMEL CONS COMP /// \sa GAME GAMEL CONS COMP
#define SYST(YEAR,NAME,PARENT,COMPAT,MACHINE,INPUT,CLASS,INIT,COMPANY,FULLNAME,FLAGS) \ #define SYST(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) \ extern game_driver const GAME_NAME(NAME) \
{ \ { \
GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \ GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \

View File

@ -51,18 +51,36 @@ enum save_error
typedef named_delegate<void ()> save_prepost_delegate; typedef named_delegate<void ()> save_prepost_delegate;
// use this to declare a given type is a simple, non-pointer type that can be /// \brief Declare a type as safe to automatically save/restore
// saved; in general, this is intended only to be used for specific enum types ///
// defined by your device /// 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) \ #define ALLOW_SAVE_TYPE(TYPE) \
template <> struct save_manager::is_atom<TYPE> : public std::true_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) \ #define ALLOW_SAVE_TYPE_AND_VECTOR(TYPE) \
ALLOW_SAVE_TYPE(TYPE) \ ALLOW_SAVE_TYPE(TYPE) \
template <> struct save_manager::is_vector_safe<TYPE> : public std::true_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 #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 // copyright-holders:Aaron Giles
/*************************************************************************** /***************************************************************************
delegate.c delegate.cpp
Templates and classes to enable delegates for callbacks. Templates and classes to enable delegates for callbacks.
***************************************************************************/ ***************************************************************************/
#include <cassert>
#include <cstdint>
#include <cstdio>
#include "delegate.h" #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 // GLOBAL VARIABLES
@ -20,7 +31,7 @@
#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE) #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 #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 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 defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64) || defined(__EMSCRIPTEN__)
if ((m_this_delta & 1) == 0) { if ((m_this_delta & 1) == 0)
#if defined(LOG_DELEGATES) {
printf("Calculated Addr = %08x\n", (uintptr_t)(void*)(m_function)); LOG("Calculated Addr = %p\n", reinterpret_cast<void const *>(uintptr_t(m_function)));
#endif
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta); object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
return reinterpret_cast<delegate_generic_function>(m_function); 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 // 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); std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
#if defined(LOG_DELEGATES) 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))));
printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1)));
#endif
return *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 #else
// apply the "this" delta to the object first // apply the "this" delta to the object first
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta); 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 the low bit of the vtable index is clear, then it is just a raw function pointer
if (!(m_function & 1)) { if (!(m_function & 1))
#if defined(LOG_DELEGATES) {
printf("Calculated Addr = %08x\n", (uintptr_t)(void*)(m_function)); LOG("Calculated Addr = %p\n", reinterpret_cast<void const *>(uintptr_t(m_function)));
#endif
return reinterpret_cast<delegate_generic_function>(m_function); return reinterpret_cast<delegate_generic_function>(m_function);
} }
// otherwise, it is the byte index into the vtable where the actual function lives // 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); std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
#if defined(LOG_DELEGATES) LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast<void *>(uintptr_t(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1))));
printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1)));
#endif
return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1); return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1);
#endif #endif
} }

View File

@ -77,9 +77,12 @@
#pragma once #pragma once
// standard C++ includes #include <any>
#include <cassert>
#include <cstdint>
#include <cstring> #include <cstring>
#include <functional> #include <exception>
#include <type_traits>
#include <typeinfo> #include <typeinfo>
#include <utility> #include <utility>
@ -571,9 +574,54 @@ class delegate<ReturnType (Params...)> : public delegate_base<ReturnType, Params
{ {
private: private:
using basetype = delegate_base<ReturnType, Params...>; 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: protected:
template <class FunctionClass> using traits = typename basetype::template traits<FunctionClass>; 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 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 <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: public:
// create a standard set of constructors
delegate() : basetype() { } 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(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) { } : 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(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) { } 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 <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) delegate &operator=(delegate const &src)
{ {
if (src.m_std_func) m_functoid = src.m_functoid;
basetype::operator=(basetype(&functional_type::operator(), &m_std_func)); m_set_functoid = src.m_set_functoid;
if (m_functoid.has_value())
m_set_functoid(*this);
else else
basetype::operator=(src); basetype::operator=(src);
m_std_func = src.m_std_func;
return *this; return *this;
} }
delegate &operator=(delegate &&src) delegate &operator=(delegate &&src)
{ {
if (src.m_std_func) m_functoid = std::move(src.m_functoid);
basetype::operator=(basetype(&functional_type::operator(), &m_std_func)); m_set_functoid = std::move(src.m_set_functoid);
if (m_functoid.has_value())
m_set_functoid(*this);
else else
basetype::operator=(std::move(src)); basetype::operator=(std::move(src));
m_std_func = std::move(src.m_std_func);
return *this; return *this;
} }
void reset() noexcept
{
basetype::operator=(basetype());
m_functoid.reset();
m_set_functoid = nullptr;
}
}; };
#endif // MAME_UTIL_DELEGATE_H #endif // MAME_UTIL_DELEGATE_H