summaryrefslogblamecommitdiffstatshomepage
path: root/src/emu/devdelegate.h
blob: bbb94930d43210182b9943534152d84db128526e (plain) (tree)
1
2
3
4
5
6
7
8
9
                       
                                           





                                                                            
                                                                            

                              
 
            


                     














                                                                            




                                                                            

                  




                                                 




                                                                                                           
          




                                                                                                                               
 
                           




                                                                                                                     
 


                                                

  

                     
 
                                         

                                                 
 
        
                                             
 
                                                    




                                                                                                                                        


                                                











                                                                                                                                                                                                                

  



                                                                               



                                                                                                                                       
 











                                                                                                                                                               

       






































































































                                                                                                                                                                  

                              
                                                                                                                   
 




                                   


  




































                                                                                             
                                
// 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 <array>
#include <functional>
#include <type_traits>
#include <utility>


namespace emu {

//**************************************************************************
//  FORWARD DECLARATIONS
//**************************************************************************

template <typename Signature, unsigned Count> class device_delegate_array;



//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

namespace detail {

// ======================> device_delegate_helper

// device_delegate_helper does non-template work
class device_delegate_helper
{
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;
};

} // namespace detail


// ======================> named_delegate
template <typename Signature>
class named_delegate : public delegate<Signature>
{
private:
	using basetype = delegate<Signature>;

	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>;

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 <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) { }

	// allow assignment
	named_delegate &operator=(named_delegate const &src) = default;

	char const *name() const { return m_name; }
};

// ======================> device_delegate

// 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;

template <typename ReturnType, typename... Params>
class device_delegate<ReturnType (Params...)> : protected named_delegate<ReturnType (Params...)>, public detail::device_delegate_helper
{
private:
	using basetype = named_delegate<ReturnType (Params...)>;

	template <class T, class U> struct is_related_device_implementation
	{ static constexpr bool value = std::is_base_of<T, U>::value && std::is_base_of<device_t, U>::value; };
	template <class T, class U> struct is_related_device_interface
	{ static constexpr bool value = std::is_base_of<T, U>::value && std::is_base_of<device_interface, U>::value && !std::is_base_of<device_t, U>::value; };
	template <class T, class U> struct is_related_device
	{ static constexpr bool value = 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:
	template <unsigned Count> using array = device_delegate_array<ReturnType (Params...), Count>;

	template <typename T> struct supports_callback
	{ static constexpr bool value = std::is_constructible<device_delegate, device_t &, char const *, T, char const *>::value; };

	// construct/assign
	explicit device_delegate(device_t &owner) : basetype(), detail::device_delegate_helper(owner) { }
	device_delegate(device_delegate const &) = default;
	device_delegate &operator=(device_delegate const &) = default;

	// construct with prototype and target
	device_delegate(basetype const &proto, device_t &object) : basetype(proto, object), detail::device_delegate_helper(object) { }
	device_delegate(device_delegate const &proto, device_t &object) : basetype(proto, object), detail::device_delegate_helper(object) { }

	// construct with base and tag
	template <class D>
	device_delegate(device_t &base, char const *tag, ReturnType (D::*funcptr)(Params...), char const *name)
		: basetype(funcptr, name, static_cast<D *>(nullptr))
		, detail::device_delegate_helper(base, tag)
	{ }
	template <class D>
	device_delegate(device_t &base, char const *tag, ReturnType (D::*funcptr)(Params...) const, char const *name)
		: basetype(funcptr, name, static_cast<D *>(nullptr))
		, detail::device_delegate_helper(base, tag)
	{ }
	template <class D>
	device_delegate(device_t &base, char const *tag, ReturnType (*funcptr)(D &, Params...), char const *name)
		: basetype(funcptr, name, static_cast<D *>(nullptr))
		, detail::device_delegate_helper(base, tag)
	{ }

	// construct with device finder
	template <class D, bool R, class E>
	device_delegate(device_finder<D, R> const &finder, ReturnType (E::*funcptr)(Params...), char const *name)
		: basetype(funcptr, name, static_cast<E *>(nullptr))
		, detail::device_delegate_helper(finder)
	{ }
	template <class D, bool R, class E>
	device_delegate(device_finder<D, R> const &finder, ReturnType (E::*funcptr)(Params...) const, char const *name)
		: basetype(funcptr, name, static_cast<E *>(nullptr))
		, detail::device_delegate_helper(finder)
	{ }
	template <class D, bool R, class E>
	device_delegate(device_finder<D, R> const &finder, ReturnType (*funcptr)(E &, Params...), char const *name)
		: basetype(funcptr, name, static_cast<E *>(nullptr))
		, detail::device_delegate_helper(finder)
	{ }

	// construct with target object
	template <class T, class D>
	device_delegate(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))
		, detail::device_delegate_helper(get_device(object))
	{ }
	template <class T, class D>
	device_delegate(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))
		, detail::device_delegate_helper(get_device(object))
	{ }
	template <class T, class D>
	device_delegate(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))
		, detail::device_delegate_helper(get_device(object))
	{ }

	// 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)
		: basetype(std::forward<T>(funcptr), name)
		, detail::device_delegate_helper(owner)
	{ basetype::operator=(basetype(std::forward<T>(funcptr), name)); }

	// 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<std::is_constructible<std::function<ReturnType (Params...)>, 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); }

	// perform the binding
	void resolve() { if (!basetype::isnull() && !basetype::has_object()) basetype::late_bind(bound_object()); }

	// accessors
	using basetype::operator();
	using basetype::isnull;
	using basetype::has_object;
	using basetype::name;
};


// 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();
	}
};

} // namespace emu


using emu::named_delegate;
using emu::device_delegate;

#endif // MAME_EMU_DEVDELEGATE_H