diff options
Diffstat (limited to 'src/emu/devdelegate.h')
-rw-r--r-- | src/emu/devdelegate.h | 441 |
1 files changed, 380 insertions, 61 deletions
diff --git a/src/emu/devdelegate.h b/src/emu/devdelegate.h index 8bdef60f880..3a63c874d92 100644 --- a/src/emu/devdelegate.h +++ b/src/emu/devdelegate.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** devdelegate.h @@ -15,98 +15,417 @@ #include "delegate.h" +#include <array> +#include <functional> +#include <type_traits> +#include <utility> + + +// older versions of libc++ are missing deduction guides that the things using this constructor require +#if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION < 10000) +namespace std { inline namespace __1 { +template<class R, class... ArgTypes > function( R(*)(ArgTypes...) ) -> function<R(ArgTypes...)>; +} } +#endif + + +namespace emu { //************************************************************************** -// TYPE DEFINITIONS +// FORWARD DECLARATIONS //************************************************************************** -// ======================> device_delegate_helper - -// device_delegate_helper does non-template work -class device_delegate_helper -{ -protected: - // constructor - device_delegate_helper(const char *devname) : m_device_name(devname) { } +template <typename Signature, unsigned Count> class device_delegate_array; - // internal helpers - delegate_late_bind &bound_object(device_t &search_root) const; - static const char *safe_tag(device_t *object); - // internal state - const char *m_device_name; -}; +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** -// ======================> named_delegate template <typename Signature> class named_delegate : public delegate<Signature> { private: using basetype = delegate<Signature>; - const char * m_name; // name string + char const *m_name = nullptr; // name string protected: template <class FunctionClass> using member_func_type = typename basetype::template 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 <typename T> using suitable_functoid = typename basetype::template suitable_functoid<T>; + public: // create a standard set of constructors - named_delegate() : basetype(), m_name(nullptr) { } - explicit named_delegate(const basetype &src) : basetype(src), m_name(src.m_name) { } - named_delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object), m_name(src.m_name) { } - template <class FunctionClass> named_delegate(member_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } - template <class FunctionClass> named_delegate(const_member_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } - explicit named_delegate(std::function<Signature> funcptr, const char *name) : basetype(funcptr), m_name(name) { } - template <class FunctionClass> named_delegate(static_ref_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } - named_delegate &operator=(const basetype &src) { basetype::operator=(src); m_name = src.m_name; return *this; } - - const char *name() const { return m_name; } + named_delegate() = default; + named_delegate(named_delegate const &) = default; + named_delegate(named_delegate const &src, delegate_late_bind &object) : basetype(src, object), m_name(src.m_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(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<suitable_functoid<T>::value, char const *> name) : basetype(std::forward<T>(funcptr)), m_name(name) { } + + // allow assignment + named_delegate &operator=(named_delegate const &) = default; + named_delegate &operator=(named_delegate &&) = default; + named_delegate &operator=(std::nullptr_t) noexcept { reset(); return *this; } + + // getters + char const *name() const noexcept { return m_name; } + + // unsetter + void reset() noexcept { basetype::reset(); m_name = nullptr; } }; -// ======================> device_delegate +template <class FunctionClass, typename ReturnType, typename... Params> +named_delegate(ReturnType (FunctionClass::*)(Params...), char const *, FunctionClass *) -> named_delegate<ReturnType (Params...)>; -// device_delegate is a delegate that wraps with a device tag and can be easily -// late bound without replicating logic everywhere -template <typename Signature> -class device_delegate : public named_delegate<Signature>, public device_delegate_helper +template <class FunctionClass, typename ReturnType, typename... Params> +named_delegate(ReturnType (FunctionClass::*)(Params...) const, char const *, FunctionClass *) -> named_delegate<ReturnType (Params...)>; + +template <class FunctionClass, typename ReturnType, typename... Params> +named_delegate(ReturnType (*)(FunctionClass &, Params...), char const *, FunctionClass *) -> named_delegate<ReturnType (Params...)>; + + +namespace detail { + +// device_delegate_helper does non-template work + +class device_delegate_helper { - using thistype = device_delegate<Signature>; - using basetype = named_delegate<Signature>; - template <class FunctionClass> using member_func_type = typename basetype::template 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>; +public: + // accessors + char const *finder_tag() const { return m_tag; } + std::pair<device_t &, char const *> finder_target() const { return std::make_pair(m_base, m_tag); } + +protected: + // construct/assign + device_delegate_helper(device_t &owner, char const *tag = nullptr) : m_base(owner), m_tag(tag) { } + template <class DeviceClass, bool Required> device_delegate_helper(device_finder<DeviceClass, Required> const &finder); + device_delegate_helper(device_delegate_helper const &) = default; + device_delegate_helper &operator=(device_delegate_helper const &) = default; + + // internal helpers + delegate_late_bind &bound_object() const; + void set_tag(device_t &object) { m_base = object; m_tag = nullptr; } + void set_tag(device_t &base, char const *tag) { m_base = base; m_tag = tag; } + void set_tag(char const *tag); + template <class DeviceClass, bool Required> void set_tag(device_finder<DeviceClass, Required> const &finder); + +private: + std::reference_wrapper<device_t> m_base; + char const *m_tag; +}; + + +// device_delegate_base doesn't need to be specialised on return type + +template <typename Signature> class device_delegate_base; + +template <typename ReturnType, typename... Params> +class device_delegate_base<ReturnType (Params...)> : protected named_delegate<ReturnType (Params...)>, public device_delegate_helper +{ +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> + using is_related_device_interface = std::bool_constant<std::is_base_of_v<T, U> && std::is_base_of_v<device_interface, U> && !std::is_base_of_v<device_t, U> >; + template <class T, class U> + using is_related_device = std::bool_constant<is_related_device_implementation<T, U>::value || is_related_device_interface<T, U>::value>; + + template <class T> static std::enable_if_t<is_related_device_implementation<T, T>::value, device_t &> get_device(T &object) { return object; } + template <class T> static std::enable_if_t<is_related_device_interface<T, T>::value, device_t &> get_device(T &object) { return object.device(); } + +public: + // construct/assign + explicit device_delegate_base(device_t &owner) : basetype(), device_delegate_helper(owner) { } + device_delegate_base(device_delegate_base const &) = default; + device_delegate_base &operator=(device_delegate_base const &) = default; + + // construct with prototype and target + device_delegate_base(basetype const &proto, device_t &object) : basetype(proto, object), device_delegate_helper(object) { } + device_delegate_base(device_delegate_base const &proto, device_t &object) : basetype(proto, object), device_delegate_helper(object) { } + + // construct with base and tag + template <class D> + device_delegate_base(device_t &base, char const *tag, ReturnType (D::*funcptr)(Params...), char const *name) + : basetype(funcptr, name, static_cast<D *>(nullptr)) + , device_delegate_helper(base, tag) + { } + template <class D> + device_delegate_base(device_t &base, char const *tag, ReturnType (D::*funcptr)(Params...) const, char const *name) + : basetype(funcptr, name, static_cast<D *>(nullptr)) + , device_delegate_helper(base, tag) + { } + template <class D> + device_delegate_base(device_t &base, char const *tag, ReturnType (*funcptr)(D &, Params...), char const *name) + : basetype(funcptr, name, static_cast<D *>(nullptr)) + , device_delegate_helper(base, tag) + { } + + // construct with device finder + template <class D, bool R, class E> + device_delegate_base(device_finder<D, R> const &finder, ReturnType (E::*funcptr)(Params...), char const *name) + : basetype(funcptr, name, static_cast<E *>(nullptr)) + , device_delegate_helper(finder) + { } + template <class D, bool R, class E> + device_delegate_base(device_finder<D, R> const &finder, ReturnType (E::*funcptr)(Params...) const, char const *name) + : basetype(funcptr, name, static_cast<E *>(nullptr)) + , device_delegate_helper(finder) + { } + template <class D, bool R, class E> + device_delegate_base(device_finder<D, R> const &finder, ReturnType (*funcptr)(E &, Params...), char const *name) + : basetype(funcptr, name, static_cast<E *>(nullptr)) + , device_delegate_helper(finder) + { } + + // construct with target object + template <class T, class D> + device_delegate_base(T &object, ReturnType (D::*funcptr)(Params...), std::enable_if_t<is_related_device<D, T>::value, char const *> name) + : basetype(funcptr, name, static_cast<D *>(&object)) + , device_delegate_helper(get_device(object)) + { } + template <class T, class D> + device_delegate_base(T &object, ReturnType (D::*funcptr)(Params...) const, std::enable_if_t<is_related_device<D, T>::value, char const *> name) + : basetype(funcptr, name, static_cast<D *>(&object)) + , device_delegate_helper(get_device(object)) + { } + template <class T, class D> + device_delegate_base(T &object, ReturnType (*funcptr)(D &, Params...), std::enable_if_t<is_related_device<D, T>::value, char const *> name) + : basetype(funcptr, name, static_cast<D *>(&object)) + , device_delegate_helper(get_device(object)) + { } + + // construct with callable object + template <typename T> + device_delegate_base(device_t &owner, T &&funcptr, std::enable_if_t<suitable_functoid<T>::value, char const *> name) + : basetype(std::forward<T>(funcptr), name) + , device_delegate_helper(owner) + { } + // setters that implicitly bind to the current device + template <class D> void set(ReturnType (D::*funcptr)(Params...), char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(nullptr); } + template <class D> void set(ReturnType (D::*funcptr)(Params...) const, char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(nullptr); } + template <class D> void set(ReturnType (*funcptr)(D &, Params...), char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(nullptr); } + + // setters that take a tag-like object specifying the target + template <typename T, class D> void set(T &&tag, ReturnType (D::*funcptr)(Params...), char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(std::forward<T>(tag)); } + template <typename T, class D> void set(T &&tag, ReturnType (D::*funcptr)(Params...) const, char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(std::forward<T>(tag)); } + template <typename T, class D> void set(T &&tag, ReturnType (*funcptr)(D &, Params...), char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(std::forward<T>(tag)); } + + // setters that take a target object + template <class T, class D> std::enable_if_t<std::is_base_of<D, T>::value> set(T &object, ReturnType (D::*funcptr)(Params...), char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(&object))); set_tag(finder_target().first); } + template <class T, class D> std::enable_if_t<std::is_base_of<D, T>::value> set(T &object, ReturnType (D::*funcptr)(Params...) const, char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(&object))); set_tag(finder_target().first); } + template <class T, class D> std::enable_if_t<std::is_base_of<D, T>::value> set(T &object, ReturnType (*funcptr)(D &, Params...), char const *name) + { 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<suitable_functoid<T>::value> set(T &&funcptr, char const *name) + { basetype::operator=(basetype(std::forward<T>(funcptr), name)); } + + // unsetter + void set(std::nullptr_t) + { reset(); } + void reset() + { basetype::reset(); set_tag(finder_target().first); } + + // accessors + using basetype::operator(); + using basetype::isnull; + using basetype::has_object; + using basetype::name; + + // perform the binding + void resolve() + { + if (!this->isnull() && !this->has_object()) + this->late_bind(bound_object()); + } +}; + +} // namespace detail + + +// device_delegate is a delegate that can be late bound to a device specification + +template <typename Signature> class device_delegate; + +template <typename ReturnType, typename... Params> +class device_delegate<ReturnType (Params...)> : protected detail::device_delegate_base<ReturnType (Params...)> +{ public: - // provide the same constructors as the base class - device_delegate() : basetype(), device_delegate_helper(nullptr) { } - device_delegate(const basetype &src) : basetype(src), device_delegate_helper(src.m_device_name) { } - device_delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object), device_delegate_helper(src.m_device_name) { } - template <class FunctionClass> device_delegate(member_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, name, object), device_delegate_helper(safe_tag(dynamic_cast<device_t *>(object))) { } - template <class FunctionClass> device_delegate(const_member_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, name, object), device_delegate_helper(safe_tag(dynamic_cast<device_t *>(object))) { } - template <class FunctionClass> device_delegate(static_ref_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, name, object), device_delegate_helper(safe_tag(dynamic_cast<device_t *>(object))) { } - device_delegate(std::function<Signature> funcptr, const char *name) : basetype(funcptr, name), device_delegate_helper(nullptr) { } - device_delegate &operator=(const thistype &src) { basetype::operator=(src); m_device_name = src.m_device_name; return *this; } - - // provide additional constructors that take a device name string - template <class FunctionClass> device_delegate(member_func_type<FunctionClass> funcptr, const char *name, const char *devname) : basetype(funcptr, name, static_cast<FunctionClass *>(nullptr)), device_delegate_helper(devname) { } - template <class FunctionClass> device_delegate(member_func_type<FunctionClass> funcptr, const char *name, const char *devname, FunctionClass *) : basetype(funcptr, name, static_cast<FunctionClass *>(nullptr)), device_delegate_helper(devname) { } - template <class FunctionClass> device_delegate(const_member_func_type<FunctionClass> funcptr, const char *name, const char *devname) : basetype(funcptr, name, static_cast<FunctionClass *>(nullptr)), device_delegate_helper(devname) { } - template <class FunctionClass> device_delegate(const_member_func_type<FunctionClass> funcptr, const char *name, const char *devname, FunctionClass *) : basetype(funcptr, name, static_cast<FunctionClass *>(nullptr)), device_delegate_helper(devname) { } - template <class FunctionClass> device_delegate(static_ref_func_type<FunctionClass> funcptr, const char *name, const char *devname, FunctionClass *) : basetype(funcptr, name, static_cast<FunctionClass *>(nullptr)), device_delegate_helper(devname) { } - device_delegate(static_ref_func_type<device_t> funcptr, const char *name) : basetype(funcptr, name, static_cast<device_t *>(nullptr)), device_delegate_helper(nullptr) { } - - // and constructors that provide a search root - device_delegate(const thistype &src, device_t &search_root) : basetype(src), device_delegate_helper(src.m_device_name) { bind_relative_to(search_root); } + template <unsigned Count> using array = device_delegate_array<ReturnType (Params...), Count>; + + template <typename T> + using supports_callback = std::bool_constant<std::is_constructible_v<device_delegate, device_t &, char const *, T, char const *> >; + + // constructors + using detail::device_delegate_base<ReturnType (Params...)>::device_delegate_base; + device_delegate(device_delegate const &) = default; + device_delegate(device_delegate const &proto, device_t &object) : detail::device_delegate_base<ReturnType (Params...)>(proto, object) { } + + // assignment + device_delegate &operator=(device_delegate const &) = default; + device_delegate &operator=(std::nullptr_t) { this->reset(); } + + // setters/unsetters + void set(device_delegate const &src) { operator=(src); } + using detail::device_delegate_base<ReturnType (Params...)>::set; + using detail::device_delegate_base<ReturnType (Params...)>::reset; + + // accessors + using detail::device_delegate_base<ReturnType (Params...)>::operator(); + using detail::device_delegate_base<ReturnType (Params...)>::isnull; + using detail::device_delegate_base<ReturnType (Params...)>::has_object; + using detail::device_delegate_base<ReturnType (Params...)>::name; + using detail::device_delegate_base<ReturnType (Params...)>::finder_tag; + using detail::device_delegate_base<ReturnType (Params...)>::finder_target; + + // perform the binding + using detail::device_delegate_base<ReturnType (Params...)>::resolve; + + template <typename T> + void resolve_safe(T &&dflt) + { + if (this->isnull()) + this->set([result = std::decay_t<T>(std::forward<T>(dflt))] (Params...) { return ReturnType(result); }, "default"); + else if (!this->has_object()) + this->late_bind(this->bound_object()); + } +}; + +template <typename... Params> +class device_delegate<void (Params...)> : protected detail::device_delegate_base<void (Params...)> +{ +public: + template <unsigned Count> using array = device_delegate_array<void (Params...), Count>; + + template <typename T> + using supports_callback = std::bool_constant<std::is_constructible_v<device_delegate, device_t &, char const *, T, char const *> >; + + // constructors + using detail::device_delegate_base<void (Params...)>::device_delegate_base; + device_delegate(device_delegate const &) = default; + device_delegate(device_delegate const &proto, device_t &object) : detail::device_delegate_base<void (Params...)>(proto, object) { } + + // assignment + device_delegate &operator=(device_delegate const &) = default; + device_delegate &operator=(std::nullptr_t) { this->reset(); } + + // setters/unsetters + void set(device_delegate const &src) { operator=(src); } + using detail::device_delegate_base<void (Params...)>::set; + using detail::device_delegate_base<void (Params...)>::reset; + + // accessors + using detail::device_delegate_base<void (Params...)>::operator(); + using detail::device_delegate_base<void (Params...)>::isnull; + using detail::device_delegate_base<void (Params...)>::has_object; + using detail::device_delegate_base<void (Params...)>::name; + using detail::device_delegate_base<void (Params...)>::finder_tag; + using detail::device_delegate_base<void (Params...)>::finder_target; // perform the binding - void bind_relative_to(device_t &search_root) { if (!basetype::isnull()) basetype::late_bind(bound_object(search_root)); } + using detail::device_delegate_base<void (Params...)>::resolve; - // getter (for validation purposes) - const char *device_name() const { return m_device_name; } + void resolve_safe() + { + if (this->isnull()) + this->set([] (Params...) { }, "default"); + else if (!this->has_object()) + this->late_bind(this->bound_object()); + } }; +// simplifies constructing an array of delegates with a single owner + +template <typename Signature, unsigned Count> +class device_delegate_array : public std::array<device_delegate<Signature>, Count> +{ +private: + template <unsigned... V> + device_delegate_array(device_t &owner, std::integer_sequence<unsigned, V...> const &) + : std::array<device_delegate<Signature>, Count>{{ make_one<V>(owner)... }} + { + } + + template <unsigned N> + device_delegate<Signature> make_one(device_t &owner) + { + return device_delegate<Signature>(owner); + } + +public: + using std::array<device_delegate<Signature>, Count>::array; + + device_delegate_array(device_t &owner) + : device_delegate_array(owner, std::make_integer_sequence<unsigned, Count>()) + { + } + + void resolve_all() + { + for (device_delegate<Signature> &elem : *this) + elem.resolve(); + } + + template <typename... T> + void resolve_all_safe(T &&... args) + { + for (device_delegate<Signature> &elem : *this) + elem.resolve_safe(args...); + } +}; + +template <class D, typename ReturnType, typename... Params> +device_delegate(device_t &, char const *, ReturnType (D::*)(Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +template <class D, typename ReturnType, typename... Params> +device_delegate(device_t &, char const *, ReturnType (D::*)(Params...) const, char const *) -> device_delegate<ReturnType (Params...)>; + +template <class D, typename ReturnType, typename... Params> +device_delegate(device_t &, char const *, ReturnType (*)(D &, Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +template <class D, bool R, class E, typename ReturnType, typename... Params> +device_delegate(device_finder<D, R> const &, ReturnType (E::*)(Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +template <class D, bool R, class E, typename ReturnType, typename... Params> +device_delegate(device_finder<D, R> const &, ReturnType (E::*)(Params...) const, char const *) -> device_delegate<ReturnType (Params...)>; + +template <class D, bool R, class E, typename ReturnType, typename... Params> +device_delegate(device_finder<D, R> const &, ReturnType (*)(E &, Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +template <class T, class D, typename ReturnType, typename... Params> +device_delegate(T &, ReturnType (D::*)(Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +template <class T, class D, typename ReturnType, typename... Params> +device_delegate(T &, ReturnType (D::*)(Params...) const, char const *) -> device_delegate<ReturnType (Params...)>; + +template <class T, class D, typename ReturnType, typename... Params> +device_delegate(T &, ReturnType (*)(D &, Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +} // namespace emu + + +using emu::named_delegate; +using emu::device_delegate; + #endif // MAME_EMU_DEVDELEGATE_H |