// license:BSD-3-Clause // copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** devdelegate.h Delegates that are late-bound to MAME devices. ***************************************************************************/ #ifndef MAME_EMU_DEVDELEGATE_H #define MAME_EMU_DEVDELEGATE_H #pragma once #include "delegate.h" #include #include #include #include // 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 function( R(*)(ArgTypes...) ) -> function; } } #endif namespace emu { //************************************************************************** // FORWARD DECLARATIONS //************************************************************************** template class device_delegate_array; //************************************************************************** // TYPE DEFINITIONS //************************************************************************** template class named_delegate : public delegate { private: using basetype = delegate; char const *m_name = nullptr; // name string protected: template using member_func_type = typename basetype::template member_func_type; template using const_member_func_type = typename basetype::template const_member_func_type; template using static_ref_func_type = typename basetype::template static_ref_func_type; template using suitable_functoid = typename basetype::template suitable_functoid; public: // create a standard set of constructors named_delegate() = default; 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 named_delegate(member_func_type funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } template named_delegate(const_member_func_type funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } template named_delegate(static_ref_func_type funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } template named_delegate(T &&funcptr, std::enable_if_t::value, char const *> name) : basetype(std::forward(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; } }; template named_delegate(ReturnType (FunctionClass::*)(Params...), char const *, FunctionClass *) -> named_delegate; template named_delegate(ReturnType (FunctionClass::*)(Params...) const, char const *, FunctionClass *) -> named_delegate; template named_delegate(ReturnType (*)(FunctionClass &, Params...), char const *, FunctionClass *) -> named_delegate; namespace detail { // device_delegate_helper does non-template work class device_delegate_helper { public: // accessors char const *finder_tag() const { return m_tag; } std::pair 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 device_delegate_helper(device_finder 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 void set_tag(device_finder const &finder); private: std::reference_wrapper m_base; char const *m_tag; }; // device_delegate_base doesn't need to be specialised on return type template class device_delegate_base; template class device_delegate_base : protected named_delegate, public device_delegate_helper { private: using basetype = named_delegate; template using suitable_functoid = typename basetype::template suitable_functoid; template using is_related_device_implementation = std::bool_constant && std::is_base_of_v >; template using is_related_device_interface = std::bool_constant && std::is_base_of_v && !std::is_base_of_v >; template using is_related_device = std::bool_constant::value || is_related_device_interface::value>; template static std::enable_if_t::value, device_t &> get_device(T &object) { return object; } template static std::enable_if_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 device_delegate_base(device_t &base, char const *tag, ReturnType (D::*funcptr)(Params...), char const *name) : basetype(funcptr, name, static_cast(nullptr)) , device_delegate_helper(base, tag) { } template device_delegate_base(device_t &base, char const *tag, ReturnType (D::*funcptr)(Params...) const, char const *name) : basetype(funcptr, name, static_cast(nullptr)) , device_delegate_helper(base, tag) { } template device_delegate_base(device_t &base, char const *tag, ReturnType (*funcptr)(D &, Params...), char const *name) : basetype(funcptr, name, static_cast(nullptr)) , device_delegate_helper(base, tag) { } // construct with device finder template device_delegate_base(device_finder const &finder, ReturnType (E::*funcptr)(Params...), char const *name) : basetype(funcptr, name, static_cast(nullptr)) , device_delegate_helper(finder) { } template device_delegate_base(device_finder const &finder, ReturnType (E::*funcptr)(Params...) const, char const *name) : basetype(funcptr, name, static_cast(nullptr)) , device_delegate_helper(finder) { } template device_delegate_base(device_finder const &finder, ReturnType (*funcptr)(E &, Params...), char const *name) : basetype(funcptr, name, static_cast(nullptr)) , device_delegate_helper(finder) { } // construct with target object template device_delegate_base(T &object, ReturnType (D::*funcptr)(Params...), std::enable_if_t::value, char const *> name) : basetype(funcptr, name, static_cast(&object)) , device_delegate_helper(get_device(object)) { } template device_delegate_base(T &object, ReturnType (D::*funcptr)(Params...) const, std::enable_if_t::value, char const *> name) : basetype(funcptr, name, static_cast(&object)) , device_delegate_helper(get_device(object)) { } template device_delegate_base(T &object, ReturnType (*funcptr)(D &, Params...), std::enable_if_t::value, char const *> name) : basetype(funcptr, name, static_cast(&object)) , device_delegate_helper(get_device(object)) { } // construct with callable object template device_delegate_base(device_t &owner, T &&funcptr, std::enable_if_t::value, char const *> name) : basetype(std::forward(funcptr), name) , device_delegate_helper(owner) { } // setters that implicitly bind to the current device template void set(ReturnType (D::*funcptr)(Params...), char const *name) { basetype::operator=(basetype(funcptr, name, static_cast(nullptr))); set_tag(nullptr); } template void set(ReturnType (D::*funcptr)(Params...) const, char const *name) { basetype::operator=(basetype(funcptr, name, static_cast(nullptr))); set_tag(nullptr); } template void set(ReturnType (*funcptr)(D &, Params...), char const *name) { basetype::operator=(basetype(funcptr, name, static_cast(nullptr))); set_tag(nullptr); } // setters that take a tag-like object specifying the target template void set(T &&tag, ReturnType (D::*funcptr)(Params...), char const *name) { basetype::operator=(basetype(funcptr, name, static_cast(nullptr))); set_tag(std::forward(tag)); } template void set(T &&tag, ReturnType (D::*funcptr)(Params...) const, char const *name) { basetype::operator=(basetype(funcptr, name, static_cast(nullptr))); set_tag(std::forward(tag)); } template void set(T &&tag, ReturnType (*funcptr)(D &, Params...), char const *name) { basetype::operator=(basetype(funcptr, name, static_cast(nullptr))); set_tag(std::forward(tag)); } // setters that take a target object template std::enable_if_t::value> set(T &object, ReturnType (D::*funcptr)(Params...), char const *name) { basetype::operator=(basetype(funcptr, name, static_cast(&object))); set_tag(finder_target().first); } template std::enable_if_t::value> set(T &object, ReturnType (D::*funcptr)(Params...) const, char const *name) { basetype::operator=(basetype(funcptr, name, static_cast(&object))); set_tag(finder_target().first); } template std::enable_if_t::value> set(T &object, ReturnType (*funcptr)(D &, Params...), char const *name) { basetype::operator=(basetype(funcptr, name, static_cast(&object))); set_tag(finder_target().first); } // setter that takes a functoid template std::enable_if_t::value> set(T &&funcptr, char const *name) { basetype::operator=(basetype(std::forward(funcptr), name)); } // unsetter void set(std::nullptr_t) { 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 class device_delegate; template class device_delegate : protected detail::device_delegate_base { public: template using array = device_delegate_array; template using supports_callback = std::bool_constant >; // constructors using detail::device_delegate_base::device_delegate_base; device_delegate(device_delegate const &) = default; device_delegate(device_delegate const &proto, device_t &object) : detail::device_delegate_base(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::set; using detail::device_delegate_base::reset; // accessors using detail::device_delegate_base::operator(); using detail::device_delegate_base::isnull; using detail::device_delegate_base::has_object; using detail::device_delegate_base::name; using detail::device_delegate_base::finder_tag; using detail::device_delegate_base::finder_target; // perform the binding using detail::device_delegate_base::resolve; template void resolve_safe(T &&dflt) { if (this->isnull()) this->set([result = std::decay_t(std::forward(dflt))] (Params...) { return ReturnType(result); }, "default"); else if (!this->has_object()) this->late_bind(this->bound_object()); } }; template class device_delegate : protected detail::device_delegate_base { public: template using array = device_delegate_array; template using supports_callback = std::bool_constant >; // constructors using detail::device_delegate_base::device_delegate_base; device_delegate(device_delegate const &) = default; device_delegate(device_delegate const &proto, device_t &object) : detail::device_delegate_base(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::set; using detail::device_delegate_base::reset; // accessors using detail::device_delegate_base::operator(); using detail::device_delegate_base::isnull; using detail::device_delegate_base::has_object; using detail::device_delegate_base::name; using detail::device_delegate_base::finder_tag; using detail::device_delegate_base::finder_target; // perform the binding using detail::device_delegate_base::resolve; 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 class device_delegate_array : public std::array, Count> { private: template device_delegate_array(device_t &owner, std::integer_sequence const &) : std::array, Count>{{ make_one(owner)... }} { } template device_delegate make_one(device_t &owner) { return device_delegate(owner); } public: using std::array, Count>::array; device_delegate_array(device_t &owner) : device_delegate_array(owner, std::make_integer_sequence()) { } void resolve_all() { for (device_delegate &elem : *this) elem.resolve(); } template void resolve_all_safe(T &&... args) { for (device_delegate &elem : *this) elem.resolve_safe(args...); } }; template device_delegate(device_t &, char const *, ReturnType (D::*)(Params...), char const *) -> device_delegate; template device_delegate(device_t &, char const *, ReturnType (D::*)(Params...) const, char const *) -> device_delegate; template device_delegate(device_t &, char const *, ReturnType (*)(D &, Params...), char const *) -> device_delegate; template device_delegate(device_finder const &, ReturnType (E::*)(Params...), char const *) -> device_delegate; template device_delegate(device_finder const &, ReturnType (E::*)(Params...) const, char const *) -> device_delegate; template device_delegate(device_finder const &, ReturnType (*)(E &, Params...), char const *) -> device_delegate; template device_delegate(T &, ReturnType (D::*)(Params...), char const *) -> device_delegate; template device_delegate(T &, ReturnType (D::*)(Params...) const, char const *) -> device_delegate; template device_delegate(T &, ReturnType (*)(D &, Params...), char const *) -> device_delegate; } // namespace emu using emu::named_delegate; using emu::device_delegate; #endif // MAME_EMU_DEVDELEGATE_H