From 7c1ba76f3b68f9a1968405bc7732d61e1ecd144f Mon Sep 17 00:00:00 2001 From: couriersud Date: Thu, 23 Feb 2017 01:14:44 +0100 Subject: [PATCH] Fix netlist stats collection. Code refactoring: Small improvement for 7493. Convert 9316 from subdevice to delegate. Convert 74107 from subdevice style to delegate. Also refactored inconsistencies in other parts of the code. (nw) --- src/devices/machine/netlist.cpp | 2 +- src/lib/netlist/devices/nld_74107.cpp | 132 ++++++------------ src/lib/netlist/devices/nld_7493.cpp | 4 +- src/lib/netlist/devices/nld_9316.cpp | 184 ++++++++----------------- src/lib/netlist/devices/nld_mm5837.cpp | 2 +- src/lib/netlist/nl_base.cpp | 14 +- src/lib/netlist/nl_base.h | 10 +- src/lib/netlist/nl_lists.h | 58 ++++++-- src/lib/netlist/plib/ppmf.h | 2 +- 9 files changed, 162 insertions(+), 246 deletions(-) diff --git a/src/devices/machine/netlist.cpp b/src/devices/machine/netlist.cpp index 93e9e020a76..cc80ab894e3 100644 --- a/src/devices/machine/netlist.cpp +++ b/src/devices/machine/netlist.cpp @@ -308,7 +308,7 @@ public: m_param[i]->setTo(v * (*m_param_mult[i])() + (*m_param_offset[i])()); } m_pos++; - m_Q.push(!m_Q.net().new_Q(), m_inc ); + m_Q.net().toggle_and_push_to_queue(m_inc); } public: diff --git a/src/lib/netlist/devices/nld_74107.cpp b/src/lib/netlist/devices/nld_74107.cpp index 45e9f56c08e..7f9e6087c91 100644 --- a/src/lib/netlist/devices/nld_74107.cpp +++ b/src/lib/netlist/devices/nld_74107.cpp @@ -16,60 +16,44 @@ namespace netlist static const netlist_time delay_107[2] = { NLTIME_FROM_NS(16), NLTIME_FROM_NS(25) }; static const netlist_time delay_107A[2] = { NLTIME_FROM_NS(15), NLTIME_FROM_NS(15) }; - NETLIB_OBJECT(74107Asub) + NETLIB_OBJECT(74107A) { - NETLIB_CONSTRUCTOR(74107Asub) - , m_clk(*this, "CLK") + NETLIB_CONSTRUCTOR(74107A) + , m_clk(*this, "CLK", NETLIB_DELEGATE(74107A, clk)) , m_Q(*this, "Q") , m_QQ(*this, "QQ") - , m_Q1(*this, "m_Q1", 0) - , m_Q2(*this, "m_Q2", 0) - , m_F(*this, "m_F", 0) , m_delay(delay_107A) + , m_J(*this, "J") + , m_K(*this, "K") + , m_clrQ(*this, "CLRQ") { } + friend class NETLIB_NAME(74107_dip); + friend class NETLIB_NAME(74107); + //friend class NETLIB_NAME(74107A_dip); + NETLIB_RESETI(); NETLIB_UPDATEI(); + NETLIB_HANDLERI(clk); - public: + private: logic_input_t m_clk; logic_output_t m_Q; logic_output_t m_QQ; - state_var m_Q1; - state_var m_Q2; - state_var m_F; - - void newstate(const netlist_sig_t state); - const netlist_time *m_delay; - }; - - NETLIB_OBJECT(74107A) - { - NETLIB_CONSTRUCTOR(74107A) - , m_sub(*this, "sub") - , m_J(*this, "J") - , m_K(*this, "K") - , m_clrQ(*this, "CLRQ") - { - register_subalias("CLK", m_sub.m_clk); - register_subalias("Q", m_sub.m_Q); - register_subalias("QQ", m_sub.m_QQ); - } - - //NETLIB_RESETI(); - NETLIB_UPDATEI(); - public: - NETLIB_SUB(74107Asub) m_sub; - logic_input_t m_J; logic_input_t m_K; logic_input_t m_clrQ; + void newstate(const netlist_sig_t state) + { + m_Q.push(state, m_delay[state]); + m_QQ.push(state ^ 1, m_delay[state ^ 1]); + } }; NETLIB_OBJECT_DERIVED(74107, 74107A) @@ -77,7 +61,7 @@ namespace netlist public: NETLIB_CONSTRUCTOR_DERIVED(74107, 74107A) { - m_sub.m_delay = delay_107; + m_delay = delay_107; } }; @@ -88,21 +72,21 @@ namespace netlist , m_2(*this, "2") { register_subalias("1", m_1.m_J); - register_subalias("2", m_1.m_sub.m_QQ); - register_subalias("3", m_1.m_sub.m_Q); + register_subalias("2", m_1.m_QQ); + register_subalias("3", m_1.m_Q); register_subalias("4", m_1.m_K); - register_subalias("5", m_2.m_sub.m_Q); - register_subalias("6", m_2.m_sub.m_QQ); + register_subalias("5", m_2.m_Q); + register_subalias("6", m_2.m_QQ); // register_subalias("7", ); ==> GND register_subalias("8", m_2.m_J); - register_subalias("9", m_2.m_sub.m_clk); + register_subalias("9", m_2.m_clk); register_subalias("10", m_2.m_clrQ); register_subalias("11", m_2.m_K); - register_subalias("12", m_1.m_sub.m_clk); + register_subalias("12", m_1.m_clk); register_subalias("13", m_1.m_clrQ); // register_subalias("14", ); ==> VCC @@ -116,69 +100,41 @@ namespace netlist NETLIB_SUB(74107) m_2; }; - NETLIB_RESET(74107Asub) + NETLIB_RESET(74107A) { m_clk.set_state(logic_t::STATE_INP_HL); //m_Q.initial(0); //m_QQ.initial(1); - - m_Q1 = 0; - m_Q2 = 0; - m_F = 0; } - inline void NETLIB_NAME(74107Asub)::newstate(const netlist_sig_t state) + NETLIB_HANDLER(74107A, clk) { - m_Q.push(state, m_delay[state]); - m_QQ.push(state ^ 1, m_delay[state ^ 1]); - } - - NETLIB_UPDATE(74107Asub) - { - const netlist_sig_t t = m_Q.net().Q(); - newstate(((t ^ 1) & m_Q1) | (t & m_Q2) | m_F); - if (m_Q1 ^ 1) + const netlist_sig_t t(m_Q.net().Q()); + /* + * J K Q1 Q2 F t Q + * 0 0 0 1 0 0 0 + * 0 1 0 0 0 0 0 + * 1 0 0 0 1 0 1 + * 1 1 1 0 0 0 1 + * 0 0 0 1 0 1 1 + * 0 1 0 0 0 1 0 + * 1 0 0 0 1 1 1 + * 1 1 1 0 0 1 0 + */ + if ((m_J() & m_K()) ^ 1) m_clk.inactivate(); + newstate(((t ^ 1) & m_J()) | (t & (m_K() ^ 1))); } NETLIB_UPDATE(74107A) { - const auto JK = (m_J() << 1) | m_K(); - - switch (JK) - { - case 0: - m_sub.m_Q1 = 0; - m_sub.m_Q2 = 1; - m_sub.m_F = 0; - m_sub.m_clk.inactivate(); - break; - case 1: // (!m_J) & m_K)) - m_sub.m_Q1 = 0; - m_sub.m_Q2 = 0; - m_sub.m_F = 0; - break; - case 2: // (m_J) & !m_K)) - m_sub.m_Q1 = 0; - m_sub.m_Q2 = 0; - m_sub.m_F = 1; - break; - case 3: // (m_J) & m_K)) - m_sub.m_Q1 = 1; - m_sub.m_Q2 = 0; - m_sub.m_F = 0; - break; - default: - break; - } - if (!m_clrQ()) { - m_sub.m_clk.inactivate(); - m_sub.newstate(0); + m_clk.inactivate(); + newstate(0); } - else if (!m_sub.m_Q2) - m_sub.m_clk.activate_hl(); + else if (m_J() | m_K()) + m_clk.activate_hl(); } NETLIB_DEVICE_IMPL(74107) diff --git a/src/lib/netlist/devices/nld_7493.cpp b/src/lib/netlist/devices/nld_7493.cpp index 89ad400580f..bc6989d5cb5 100644 --- a/src/lib/netlist/devices/nld_7493.cpp +++ b/src/lib/netlist/devices/nld_7493.cpp @@ -52,9 +52,9 @@ namespace netlist if (m_reset) { m_bcd = (m_bcd + 1) & 0x07; - m_QB.push(m_bcd & 1, out_delay); - m_QC.push((m_bcd >> 1) & 1, out_delay2); m_QD.push((m_bcd >> 2) & 1, out_delay3); + m_QC.push((m_bcd >> 1) & 1, out_delay2); + m_QB.push(m_bcd & 1, out_delay); } } diff --git a/src/lib/netlist/devices/nld_9316.cpp b/src/lib/netlist/devices/nld_9316.cpp index 877d2da1a82..61ae427a2a8 100644 --- a/src/lib/netlist/devices/nld_9316.cpp +++ b/src/lib/netlist/devices/nld_9316.cpp @@ -14,64 +14,47 @@ namespace netlist { namespace devices { - NETLIB_OBJECT(9316_subABCD) + + NETLIB_OBJECT(9316) { - NETLIB_CONSTRUCTOR(9316_subABCD) - , m_A(*this, "A") - , m_B(*this, "B") - , m_C(*this, "C") - , m_D(*this, "D") - { - } - - //NETLIB_RESETI() - //NETLIB_UPDATEI(); - - public: - logic_input_t m_A; - logic_input_t m_B; - logic_input_t m_C; - logic_input_t m_D; - - unsigned read_ABCD() const - { - return (m_D() << 3) | (m_C() << 2) | (m_B() << 1) | (m_A() << 0); - } - }; - - NETLIB_OBJECT(9316_sub) - { - NETLIB_CONSTRUCTOR(9316_sub) - , m_CLK(*this, "CLK") + NETLIB_CONSTRUCTOR(9316) + , m_CLK(*this, "CLK", NETLIB_DELEGATE(9316, clk)) , m_cnt(*this, "m_cnt", 0) - , m_loadq(*this, "m_loadq", 0) - , m_ent(*this, "m_ent", 0) + , m_ENP(*this, "ENP") + , m_ENT(*this, "ENT") + , m_CLRQ(*this, "CLRQ") + , m_LOADQ(*this, "LOADQ") + , m_A(*this, "A", NETLIB_DELEGATE(9316, noop)) + , m_B(*this, "B", NETLIB_DELEGATE(9316, noop)) + , m_C(*this, "C", NETLIB_DELEGATE(9316, noop)) + , m_D(*this, "D", NETLIB_DELEGATE(9316, noop)) , m_QA(*this, "QA") , m_QB(*this, "QB") , m_QC(*this, "QC") , m_QD(*this, "QD") , m_RC(*this, "RC") - , m_ABCD(nullptr) { } NETLIB_RESETI(); NETLIB_UPDATEI(); + NETLIB_HANDLERI(clk); + NETLIB_HANDLERI(noop) { } - public: - void update_outputs_all(const unsigned cnt, const netlist_time out_delay) - { - m_QA.push((cnt >> 0) & 1, out_delay); - m_QB.push((cnt >> 1) & 1, out_delay); - m_QC.push((cnt >> 2) & 1, out_delay); - m_QD.push((cnt >> 3) & 1, out_delay); - } - + protected: logic_input_t m_CLK; state_var m_cnt; - state_var m_loadq; - state_var m_ent; + + logic_input_t m_ENP; + logic_input_t m_ENT; + logic_input_t m_CLRQ; + logic_input_t m_LOADQ; + + logic_input_t m_A; + logic_input_t m_B; + logic_input_t m_C; + logic_input_t m_D; logic_output_t m_QA; logic_output_t m_QB; @@ -79,45 +62,15 @@ namespace netlist logic_output_t m_QD; logic_output_t m_RC; - NETLIB_NAME(9316_subABCD) *m_ABCD; - }; - NETLIB_OBJECT(9316) - { - NETLIB_CONSTRUCTOR(9316) - , sub(*this, "sub") - , subABCD(*this, "subABCD") - , m_ENP(*this, "ENP") - , m_ENT(*this, "ENT") - , m_CLRQ(*this, "CLRQ") - , m_LOADQ(*this, "LOADQ") + private: + void update_outputs_all(const unsigned &cnt, const netlist_time &out_delay) { - sub.m_ABCD = &(subABCD); - - register_subalias("CLK", sub.m_CLK); - - register_subalias("A", subABCD.m_A); - register_subalias("B", subABCD.m_B); - register_subalias("C", subABCD.m_C); - register_subalias("D", subABCD.m_D); - - register_subalias("QA", sub.m_QA); - register_subalias("QB", sub.m_QB); - register_subalias("QC", sub.m_QC); - register_subalias("QD", sub.m_QD); - register_subalias("RC", sub.m_RC); + m_QA.push((cnt >> 0) & 1, out_delay); + m_QB.push((cnt >> 1) & 1, out_delay); + m_QC.push((cnt >> 2) & 1, out_delay); + m_QD.push((cnt >> 3) & 1, out_delay); } - - NETLIB_RESETI(); - NETLIB_UPDATEI(); - - protected: - NETLIB_SUB(9316_sub) sub; - NETLIB_SUB(9316_subABCD) subABCD; - logic_input_t m_ENP; - logic_input_t m_ENT; - logic_input_t m_CLRQ; - logic_input_t m_LOADQ; }; NETLIB_OBJECT_DERIVED(9316_dip, 9316) @@ -125,90 +78,65 @@ namespace netlist NETLIB_CONSTRUCTOR_DERIVED(9316_dip, 9316) { register_subalias("1", m_CLRQ); - register_subalias("2", sub.m_CLK); - register_subalias("3", subABCD.m_A); - register_subalias("4", subABCD.m_B); - register_subalias("5", subABCD.m_C); - register_subalias("6", subABCD.m_D); + register_subalias("2", m_CLK); + register_subalias("3", m_A); + register_subalias("4", m_B); + register_subalias("5", m_C); + register_subalias("6", m_D); register_subalias("7", m_ENP); // register_subalias("8", ); -. GND register_subalias("9", m_LOADQ); register_subalias("10", m_ENT); - register_subalias("11", sub.m_QD); - register_subalias("12", sub.m_QC); - register_subalias("13", sub.m_QB); - register_subalias("14", sub.m_QA); - register_subalias("15", sub.m_RC); + register_subalias("11", m_QD); + register_subalias("12", m_QC); + register_subalias("13", m_QB); + register_subalias("14", m_QA); + register_subalias("15", m_RC); // register_subalias("16", ); -. VCC } }; NETLIB_RESET(9316) - { - sub.do_reset(); - subABCD.do_reset(); - } - - NETLIB_RESET(9316_sub) { m_CLK.set_state(logic_t::STATE_INP_LH); m_cnt = 0; - m_loadq = 1; - m_ent = 1; } - NETLIB_UPDATE(9316_sub) + NETLIB_HANDLER(9316, clk) { - if (m_loadq) + if (m_LOADQ()) { - if (m_cnt < MAXCNT - 1) - { - m_cnt++; - update_outputs_all(m_cnt, NLTIME_FROM_NS(20)); - } - else if (m_cnt == MAXCNT - 1) - { - m_cnt = MAXCNT; - m_RC.push(m_ent, NLTIME_FROM_NS(27)); - m_QA.push(1, NLTIME_FROM_NS(20)); - } - else // MAXCNT - { - m_RC.push(0, NLTIME_FROM_NS(27)); - m_cnt = 0; - update_outputs_all(m_cnt, NLTIME_FROM_NS(20)); - } + m_cnt = (m_cnt < MAXCNT ? m_cnt + 1 : 0); + update_outputs_all(m_cnt, NLTIME_FROM_NS(20)); + m_RC.push(m_ENT() & (m_cnt == MAXCNT), NLTIME_FROM_NS(27)); } else { - m_cnt = m_ABCD->read_ABCD(); - m_RC.push(m_ent & (m_cnt == MAXCNT), NLTIME_FROM_NS(27)); + m_cnt = (m_D() << 3) | (m_C() << 2) | (m_B() << 1) | (m_A() << 0); + m_RC.push(m_ENT() & (m_cnt == MAXCNT), NLTIME_FROM_NS(27)); update_outputs_all(m_cnt, NLTIME_FROM_NS(22)); } } NETLIB_UPDATE(9316) { - sub.m_loadq = m_LOADQ(); - sub.m_ent = m_ENT(); const netlist_sig_t clrq = m_CLRQ(); - if (((sub.m_loadq ^ 1) | (sub.m_ent & m_ENP())) & clrq) + if (((m_LOADQ() ^ 1) | (m_ENT() & m_ENP())) & clrq) { - sub.m_CLK.activate_lh(); - sub.m_RC.push(sub.m_ent & (sub.m_cnt == MAXCNT), NLTIME_FROM_NS(27)); + m_CLK.activate_lh(); + m_RC.push(m_ENT() & (m_cnt == MAXCNT), NLTIME_FROM_NS(27)); } else { - sub.m_CLK.inactivate(); - if (!clrq && (sub.m_cnt>0)) + m_CLK.inactivate(); + if (!clrq && (m_cnt>0)) { - sub.update_outputs_all(0, NLTIME_FROM_NS(36)); - sub.m_cnt = 0; - //return; + update_outputs_all(0, NLTIME_FROM_NS(36)); + m_cnt = 0; } - sub.m_RC.push(sub.m_ent & (sub.m_cnt == MAXCNT), NLTIME_FROM_NS(27)); + m_RC.push(m_ENT() & (m_cnt == MAXCNT), NLTIME_FROM_NS(27)); } } diff --git a/src/lib/netlist/devices/nld_mm5837.cpp b/src/lib/netlist/devices/nld_mm5837.cpp index 6d2931ba3af..1b57b9c8ece 100644 --- a/src/lib/netlist/devices/nld_mm5837.cpp +++ b/src/lib/netlist/devices/nld_mm5837.cpp @@ -87,7 +87,7 @@ namespace netlist NETLIB_UPDATE(MM5837_dip) { - m_Q.push(!m_feedback.Q(), m_inc); + m_Q.push(!m_feedback(), m_inc); /* shift register * diff --git a/src/lib/netlist/nl_base.cpp b/src/lib/netlist/nl_base.cpp index e2d6ff3d032..e26aef72233 100644 --- a/src/lib/netlist/nl_base.cpp +++ b/src/lib/netlist/nl_base.cpp @@ -553,7 +553,9 @@ void netlist_t::process_queue(const netlist_time &delta) NL_NOEXCEPT const netlist_time inc = m_mainclock->m_inc; netlist_time mc_time(mc_net.time()); - while (1) + detail::queue_t::entry_t e; + + do { while (m_queue.top().m_exec_time > mc_time) { @@ -563,16 +565,14 @@ void netlist_t::process_queue(const netlist_time &delta) NL_NOEXCEPT mc_time += inc; } - const detail::queue_t::entry_t e(m_queue.pop()); + e = m_queue.pop(); m_time = e.m_exec_time; if (e.m_object != nullptr) { e.m_object->update_devs(); m_perf_out_processed.inc(); } - else - break; - } + } while (e.m_object != nullptr); mc_net.set_time(mc_time); } m_stat_mainloop.stop(); @@ -862,7 +862,11 @@ void detail::net_t::update_devs() NL_NOEXCEPT { p.device().m_stat_call_count.inc(); if ((p.state() & mask) != 0) + { + p.device().m_stat_total_time.start(); p.m_delegate(); + p.device().m_stat_total_time.stop(); + } } } diff --git a/src/lib/netlist/nl_base.h b/src/lib/netlist/nl_base.h index 16a6f30212f..f8f6189f5b9 100644 --- a/src/lib/netlist/nl_base.h +++ b/src/lib/netlist/nl_base.h @@ -636,11 +636,8 @@ namespace netlist nldelegate delegate = nldelegate()); virtual ~logic_input_t(); - netlist_sig_t Q() const NL_NOEXCEPT; - netlist_sig_t operator()() const NL_NOEXCEPT { - nl_assert(state() != STATE_INP_PASSIVE); return Q(); } @@ -648,7 +645,8 @@ namespace netlist void activate() NL_NOEXCEPT; void activate_hl() NL_NOEXCEPT; void activate_lh() NL_NOEXCEPT; - + private: + netlist_sig_t Q() const NL_NOEXCEPT; }; // ----------------------------------------------------------------------------- @@ -768,7 +766,6 @@ namespace netlist virtual ~logic_net_t(); netlist_sig_t Q() const NL_NOEXCEPT { return m_cur_Q; } - netlist_sig_t new_Q() const NL_NOEXCEPT { return m_new_Q; } void initial(const netlist_sig_t val) NL_NOEXCEPT { m_cur_Q = m_new_Q = val; } void set_Q_and_push(const netlist_sig_t newQ, const netlist_time delay) NL_NOEXCEPT @@ -1059,9 +1056,7 @@ namespace netlist void update_dev() NL_NOEXCEPT { - m_stat_total_time.start(); do_update(); - m_stat_total_time.stop(); } void do_inc_active() NL_NOEXCEPT @@ -1438,6 +1433,7 @@ namespace netlist inline netlist_sig_t logic_input_t::Q() const NL_NOEXCEPT { + nl_assert(state() != STATE_INP_PASSIVE); return net().Q(); } diff --git a/src/lib/netlist/nl_lists.h b/src/lib/netlist/nl_lists.h index 618b1b68d92..8417d1231dc 100644 --- a/src/lib/netlist/nl_lists.h +++ b/src/lib/netlist/nl_lists.h @@ -131,8 +131,26 @@ namespace netlist void retime(const Element &elem, const Time t) noexcept { - remove(elem); - push(entry_t(t, elem)); + /* Lock */ + tqlock lck(m_lock); + for (entry_t * i = m_end - 1; i > &m_list[0]; i--) + { + if (i->m_object == elem) + { + i->m_exec_time = t; + while ((i-1)->m_exec_time < i->m_exec_time) + { + std::swap(*(i-1), *i); + i--; + } + while (i < m_end && (i+1)->m_exec_time > i->m_exec_time) + { + std::swap(*(i+1), *i); + i++; + } + return; + } + } } void clear() @@ -212,28 +230,28 @@ namespace netlist }; timed_queue(const std::size_t list_size) + : m_list(list_size) { - m_list.reserve(list_size); clear(); } constexpr std::size_t capacity() const noexcept { return m_list.capacity(); } - constexpr bool empty() const noexcept { return m_list.empty(); } + constexpr bool empty() const noexcept { return &m_list[0] == m_end; } void push(entry_t &&e) noexcept { /* Lock */ tqlock lck(m_lock); - m_list.push_back(e); - std::push_heap(m_list.begin(), m_list.end(), compare()); + *m_end++ = e; + std::push_heap(&m_list[0], m_end, compare()); m_prof_call.inc(); } entry_t pop() noexcept { entry_t t(m_list[0]); - std::pop_heap(m_list.begin(), m_list.end(), compare()); - m_list.pop_back(); + std::pop_heap(&m_list[0], m_end, compare()); + m_end--; return t; } @@ -243,26 +261,39 @@ namespace netlist { /* Lock */ tqlock lck(m_lock); - for (entry_t * i = &m_list[m_list.size() - 1]; i >= &m_list[0]; i--) + for (entry_t * i = m_end - 1; i >= &m_list[0]; i--) { if (i->m_object == elem) { - m_list.erase(m_list.begin() + (i - &m_list[0])); - std::make_heap(m_list.begin(), m_list.end(), compare()); + m_end--; + for (;i < m_end; i++) + *i = std::move(*(i+1)); + std::make_heap(&m_list[0], m_end, compare()); + return; } } } void retime(const Element &elem, const Time t) noexcept { - remove(elem); - push(entry_t(t, elem)); + /* Lock */ + tqlock lck(m_lock); + for (entry_t * i = m_end - 1; i >= &m_list[0]; i--) + { + if (i->m_object == elem) + { + i->m_exec_time = t; + std::make_heap(&m_list[0], m_end, compare()); + return; + } + } } void clear() { tqlock lck(m_lock); m_list.clear(); + m_end = &m_list[0]; } // save state support & mame disasm @@ -280,6 +311,7 @@ namespace netlist tqmutex m_lock; std::vector m_list; + entry_t *m_end; public: // profiling diff --git a/src/lib/netlist/plib/ppmf.h b/src/lib/netlist/plib/ppmf.h index 6c482e7a72d..1c18328a374 100644 --- a/src/lib/netlist/plib/ppmf.h +++ b/src/lib/netlist/plib/ppmf.h @@ -208,7 +208,7 @@ namespace plib { #endif } template - inline R call(O *obj, Targs... args) const + R call(O *obj, Targs... args) const { using function_ptr = MEMBER_ABI R (*)(O *obj, Targs... args); return (reinterpret_cast(m_func))(obj, std::forward(args)...);