summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2021-09-16 00:28:09 +1000
committer Vas Crabb <cuavas@users.noreply.github.com>2021-09-16 02:02:45 +1000
commitd334586156dabf0db220b843c81f2a54cc3a2ece (patch)
treed853270b7b8081769700703d50814794c54a0209
parent4a39d895af7f22ce57aeebab588845cb3f7a62d6 (diff)
-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.
-rw-r--r--scripts/target/mame/tiny.lua1
-rw-r--r--src/emu/devdelegate.h21
-rw-r--r--src/emu/device.h183
-rw-r--r--src/emu/emumem.h2
-rw-r--r--src/emu/gamedrv.h10
-rw-r--r--src/emu/save.h28
-rw-r--r--src/lib/util/delegate.cpp43
-rw-r--r--src/lib/util/delegate.h128
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 <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()); }
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<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;
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 <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);
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<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
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 <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
}
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 <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