diff options
Diffstat (limited to 'src/lib/netlist/plib/ppmf.h')
-rw-r--r-- | src/lib/netlist/plib/ppmf.h | 721 |
1 files changed, 514 insertions, 207 deletions
diff --git a/src/lib/netlist/plib/ppmf.h b/src/lib/netlist/plib/ppmf.h index 9ee759befa4..56f40bde922 100644 --- a/src/lib/netlist/plib/ppmf.h +++ b/src/lib/netlist/plib/ppmf.h @@ -1,280 +1,587 @@ -// license:GPL-2.0+ +// license:BSD-3-Clause // copyright-holders:Couriersud -/* - * ppmf.h - * - */ #ifndef PPMF_H_ #define PPMF_H_ +/// +/// \file ppmf.h +/// +/// +/// PMF_TYPE_PMF +/// Use standard pointer to member function syntax C++11 +/// +/// PMF_TYPE_GNUC_PMF_CONV +/// Use gnu extension and convert the pmf to a function pointer. +/// This is not standard compliant and needs +/// -Wno-pmf-conversions to compile. +/// +/// PMF_TYPE_INTERNAL_* +/// Use the same approach as MAME for deriving the function pointer. +/// This is compiler-dependent as well +/// +/// Benchmarks for `./nltool -c run -t 10 -n pong src/mame/machine/nl_pong.cpp` +/// +/// PMF_TYPE_INTERNAL: 215% 215% 564% 580% +/// PMF_TYPE_GNUC_PMF: 163% 196% 516% 490% +/// PMF_TYPE_GNUC_PMF_CONV: 215% 215% 560% 575% +/// +/// + +/// \brief Enable experimental code on Visual Studio builds and VS clang llvm builds +/// +/// This enables experimental code which uses optimized builds the +/// ppmf_type::INTERNAL_MSC path also for complex (struct/union) return types. +/// This currently depends on whether the code can adequately determine on +/// x64 builds if the return type is returned through registers or passed as a +/// second argument as a pointer to the member function. +/// +/// The experimental code uses a temporary storage for the return value. This a +/// copy overhead for causes for large sized return types a copy overhead. +/// It would be easier if we would be able to obtain the RDX register on entry +/// to the call. On MSVC this seems not to be possible since on x64 inline +/// assembly is not supported. Even with clang-cl inline assembly this was not +/// successful when optimized code was compiled. Therefore we have to live with +/// the limitations. +/// +/// This code path is disabled by default currently. +/// +#if !defined(PPMF_EXPERIMENTAL) +#define PPMF_EXPERIMENTAL 0 +#endif + +/// brief Enable using MAME delegates as a replacement for ppmf. +/// +/// This define enables the use of MAME delegates (src/lib/util/delegate.h +/// as a replacement to ppmf. Enable this setting if you want to use the nltool +/// test suite (nltool -c tests) to produce comparisons to ppmf. +/// +#if !defined(PPMF_USE_MAME_DELEGATES) +#define PPMF_USE_MAME_DELEGATES 0 +#endif + +#if PPMF_USE_MAME_DELEGATES + +#include "../../util/delegate.h" + +namespace plib { + template<typename Signature> + class pmfp : public delegate<Signature> + { + public: + using basetype = delegate<Signature>; + using basetype::basetype; + using basetype::object; + explicit operator bool() const { return !this->isnull(); } + }; +} +#else + #include "pconfig.h" +#include "ptypes.h" +#include <algorithm> +#include <cstddef> // ptrdiff_t #include <cstdint> // uintptr_t +#include <type_traits> #include <utility> -/* - * - * NL_PMF_TYPE_GNUC_PMF - * Use standard pointer to member function syntax C++11 - * - * NL_PMF_TYPE_GNUC_PMF_CONV - * Use gnu extension and convert the pmf to a function pointer. - * This is not standard compliant and needs - * -Wno-pmf-conversions to compile. - * - * NL_PMF_TYPE_INTERNAL - * Use the same approach as MAME for deriving the function pointer. - * This is compiler-dependent as well - * - * Benchmarks for ./nltool -c run -f src/mame/machine/nl_pong.cpp -t 10 -n pong_fast - * - * NL_PMF_TYPE_INTERNAL: 215% 215% - * NL_PMF_TYPE_GNUC_PMF: 163% 196% - * NL_PMF_TYPE_GNUC_PMF_CONV: 215% 215% - * NL_PMF_TYPE_VIRTUAL: 213% 209% - * - * The whole exercise was done to avoid virtual calls. In prior versions of - * netlist, the INTERNAL and GNUC_PMF_CONV approach provided significant improvement. - * Since than, "hot" was removed from functions declared as virtual. - * This may explain that the recent benchmarks show no difference at all. - * - */ - -#if (PPMF_TYPE == PPMF_TYPE_GNUC_PMF_CONV) -#pragma GCC diagnostic ignored "-Wpmf-conversions" -#endif +//============================================================ +// Macro magic +//============================================================ -#if defined(__GNUC__) && (__GNUC__ > 6) -#pragma GCC diagnostic ignored "-Wnoexcept-type" +//#define PPMF_FORCE_TYPE 1 + +#ifndef PPMF_FORCE_TYPE +#define PPMF_FORCE_TYPE -1 #endif namespace plib { -/* - * The following class was derived from the MAME delegate.h code. - * It derives a pointer to a member function. - */ -#if (PHAS_PMF_INTERNAL > 0) - class mfp + enum class ppmf_type + { + PMF, + GNUC_PMF_CONV, + INTERNAL_ITANIUM, + INTERNAL_ARM, + INTERNAL_MSC + }; + + + struct ppmf_internal_selector + { + using ci = compile_info; + constexpr static ppmf_type value = + (PPMF_FORCE_TYPE >= 0) ? static_cast<ppmf_type>(PPMF_FORCE_TYPE) : + (ci::type() == ci_compiler::CLANG && !ci::m64() + && ci::os() == ci_os::WINDOWS) ? ppmf_type::PMF : + (ci::mingw() && !ci::m64() && ci::version::full() >= typed_version<4,7>::full()) ? ppmf_type::PMF : + (ci::mingw() && !ci::m64()) ? ppmf_type::PMF : // Dropped support for mingw32 < 407 ppmf_type::INTERNAL_ITANIUM : + (ci::env() == ci_env::MSVC && ci::m64()) ? ppmf_type::INTERNAL_MSC : + ((ci::type() == ci_compiler::CLANG || ci::type() == ci_compiler::GCC) + && (ci::arch() == ci_arch::MIPS + || ci::arch() == ci_arch::ARM + || ci::os() == ci_os::EMSCRIPTEN)) ? ppmf_type::INTERNAL_ARM : + (ci::type() == ci_compiler::CLANG || ci::type() == ci_compiler::GCC) ? ppmf_type::INTERNAL_ITANIUM : + ppmf_type::PMF + ; + }; + + static_assert(!(compile_info::type() == ci_compiler::CLANG && ppmf_internal_selector::value == (ppmf_type::GNUC_PMF_CONV)), "clang does not support ppmf_type::GNUC_PMF_CONV"); + static_assert(!(compile_info::env() == ci_env::NVCC && ppmf_internal_selector::value == (ppmf_type::GNUC_PMF_CONV)), "nvcc does not support ppmf_type::GNUC_PMF_CONV"); + + template<typename R, typename... Targs> + struct mfp_traits + { + template<typename C> using specific_member_function = R (C::*)(Targs...); + template<typename C> using const_specific_member_function = R (C::*)(Targs...) const; + template<typename C> using member_static_ref = R (*)(C &, Targs...); + template<typename C> using member_static_ptr = R (*)(C *, Targs...); + }; + + class mfp_generic_class; + + + /// + /// \brief Used to derive a pointer to a member function. + /// + /// The following class was derived from the MAME delegate.h code. + /// + template <ppmf_type PMFINTERNAL> + class mfp_raw; + + template <> + class mfp_raw<ppmf_type::INTERNAL_ITANIUM> { public: // construct from any member function pointer -#ifdef _MSC_VER - class __single_inheritance si_generic_class; - class generic_class { }; -#else - class generic_class; -#endif using generic_function = void (*)(); template<typename MemberFunctionType> - mfp(MemberFunctionType mftp) // NOLINT(cppcoreguidelines-pro-type-member-init) + mfp_raw(MemberFunctionType mftp); - : m_function(0), m_this_delta(0), m_size(sizeof(mfp)) - { - *reinterpret_cast<MemberFunctionType *>(this) = mftp; - } + // extract the generic function and adjust the object pointer + void convert_to_generic(generic_function &func, mfp_generic_class *&object) const; + + /// \brief Byte offset into the vtable + /// + /// On x86-64, the vtable contains pointers to code, and function pointers + /// are pointers to code. To obtain a function pointer for a virtual + /// member function, you fetch a pointer to code from the vtable. + /// + /// On traditional PPC64, the vtable contains pointers to function + /// descriptors, and function pointers are pointers to function descriptors. + /// To obtain a function pointer for a virtual member function, you + /// fetch a pointer to a function descriptor from the vtable. + /// + /// On IA64, the vtable contains function descriptors, and function + /// pointers are pointers to function descriptors. To obtain a + /// function pointer for a virtual member function, you calculate + /// the address of the function descriptor in the vtable. + /// + /// Simply adding the byte offset to the vtable pointer creates a + /// function pointer on IA64 because the vtable contains function + /// descriptors; on most other targets, the vtable contains function + /// pointers, so you need to fetch the function pointer after + /// calculating its address in the vtable. + /// + uintptr_t m_function; // first item can be one of two things: + // if even, it's a function pointer + // if odd, it's the byte offset into the vtable + ptrdiff_t m_this_delta; // delta to apply to the 'this' pointer + }; - template<typename MemberFunctionType, typename FunctionType, typename ObjectType> - static void get_mfp(MemberFunctionType mftp, FunctionType &func, ObjectType *&object) - { - mfp mfpo(mftp); - //return mfpo.update_after_bind<FunctionType>(object); - generic_function rfunc(nullptr); - auto robject = reinterpret_cast<generic_class *>(object); - mfpo.convert_to_generic(rfunc, robject); - func = reinterpret_cast<FunctionType>(rfunc); - object = reinterpret_cast<ObjectType *>(robject); - } + template <> + class mfp_raw<ppmf_type::INTERNAL_ARM> + { + public: + // construct from any member function pointer + using generic_function = void (*)(); + + template<typename MemberFunctionType> + mfp_raw(MemberFunctionType mftp); + + // extract the generic function and adjust the object pointer + void convert_to_generic(generic_function &func, mfp_generic_class *&object) const; + + // actual state + uintptr_t m_function; // first item can be a function pointer or a byte offset into the vtable + ptrdiff_t m_this_delta; // delta to apply to the 'this' pointer after right shifting by one bit + // if even, m_function is a fuction pointer + // if odd, m_function is the byte offset into the vtable + }; + + template <> + class mfp_raw<ppmf_type::INTERNAL_MSC> + { + public: + // construct from any member function pointer + using generic_function = void (*)(); + struct unknown_base_equiv { generic_function fptr; int thisdisp, vptrdisp, vtdisp; }; + struct single_base_equiv { generic_function fptr; }; + + template<typename MemberFunctionType> + mfp_raw(MemberFunctionType mftp); - private: // extract the generic function and adjust the object pointer - void convert_to_generic(generic_function &func, generic_class *&object) const + void convert_to_generic(generic_function &func, mfp_generic_class *&object) const; + + // actual state + uintptr_t m_function; // pointer to the function + int m_this_delta; // delta to apply to the 'this' pointer + + int m_vptr_index; // index into the vptr table. + int m_vt_index; // offset to be applied after vptr table lookup. + unsigned m_size; + }; + + template <typename R> + using pmf_is_register_return_type = std::integral_constant<bool, + std::is_void_v<R> || + std::is_scalar_v<R> || + std::is_reference_v<R> || + std::is_same_v<std::remove_cv_t<R>, compile_info::int128_type> || + std::is_same_v<std::remove_cv_t<R>, compile_info::uint128_type> >; + + template<ppmf_type PMFINTERNAL, typename R, typename... Targs> + struct mfp_helper + { + protected: + static_assert(PMFINTERNAL >= ppmf_type::INTERNAL_ITANIUM && PMFINTERNAL <= ppmf_type::INTERNAL_MSC, "Invalid PMF type"); + + using traits = mfp_traits<R, Targs...>; + using generic_member_function = typename traits::template specific_member_function<mfp_generic_class>; + using generic_member_abi_function = typename traits::template member_static_ptr<mfp_generic_class>; + + using raw_type = mfp_raw<PMFINTERNAL>; + using generic_function_storage = typename raw_type::generic_function; + + mfp_helper(); + + template<typename O, typename F> + void bind(O *object, F *mftp); + + R call(Targs&&... args) const noexcept(true) { - if (PHAS_PMF_INTERNAL == 1) +#if defined(_MSC_VER) && (PPMF_EXPERIMENTAL) + if constexpr (pmf_is_register_return_type<R>::value) { - // apply the "this" delta to the object first - // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) - auto o_p_delta = reinterpret_cast<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)) - func = reinterpret_cast<generic_function>(m_function); - else - { - // otherwise, it is the byte index into the vtable where the actual function lives - std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(o_p_delta); - func = *reinterpret_cast<generic_function *>(vtable_base + m_function - 1); - } - object = o_p_delta; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const auto* func = reinterpret_cast<const generic_member_abi_function*>(&m_resolved); + return (*func)(m_obj, std::forward<Targs>(args)...); } - else if (PHAS_PMF_INTERNAL == 2) + else { - if ((m_this_delta & 1) == 0) { - object = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta); - func = reinterpret_cast<generic_function>(m_function); - } - else - { - object = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object)); - - // 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); - func = *reinterpret_cast<generic_function *>(vtable_base + m_function + m_this_delta - 1); - } + using generic_member_abi_function_alt = void (*)(mfp_generic_class *,void *, Targs...); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const auto* func = reinterpret_cast<const generic_member_abi_function_alt*>(&m_resolved); + std::uint8_t temp[sizeof(typename std::conditional<std::is_void_v<R>, void *, R>::type)]; + (*func)(m_obj, &temp[0], std::forward<Targs>(args)...); + return *reinterpret_cast<R *>(&temp); } - else if (PHAS_PMF_INTERNAL == 3) - { - const int SINGLE_MEMFUNCPTR_SIZE = sizeof(void (generic_class::*)()); +#else + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const auto* func = reinterpret_cast<const generic_member_abi_function*>(&m_resolved); + return (*func)(m_obj, std::forward<Targs>(args)...); +#endif + } - func = reinterpret_cast<generic_function>(m_function); - if (m_size == SINGLE_MEMFUNCPTR_SIZE + sizeof(int)) - object = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta); - } + generic_function_storage m_resolved; + mfp_generic_class *m_obj; + }; + + template<typename R, typename... Targs> + struct mfp_helper<ppmf_type::PMF, R, Targs...> + { + protected: + using traits = mfp_traits<R, Targs...>; + using generic_member_function = typename traits::template specific_member_function<mfp_generic_class>; + + template <class C> + using member_abi_function = typename traits::template specific_member_function<C>; + mfp_helper(); + + template<typename O, typename F> + void bind(O *object, F *mftp); + + R call(Targs&&... args) const noexcept(true) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto* func = reinterpret_cast<const generic_member_function *>(&m_resolved); + return (*m_stub)(func, m_obj, std::forward<Targs>(args)...); } - // actual state - uintptr_t m_function; // first item can be one of two things: - // if even, it's a pointer to the function - // if odd, it's the byte offset into the vtable - int m_this_delta; // delta to apply to the 'this' pointer - - int m_dummy1; // only used for visual studio x64 - int m_dummy2; - int m_size; + generic_member_function m_resolved; + mfp_generic_class *m_obj; + R (*m_stub)(const generic_member_function *funci, mfp_generic_class *obji, Targs&&... args); + + private: + template<typename O> + static R stub(const generic_member_function* funci, mfp_generic_class* obji, Targs&&... args) noexcept(true); }; -#endif -#if (PPMF_TYPE == PPMF_TYPE_PMF) template<typename R, typename... Targs> - class pmfp_base + struct mfp_helper<ppmf_type::GNUC_PMF_CONV, R, Targs...> { + protected: + using traits = mfp_traits<R, Targs...>; + + template <class C> + using member_abi_function = typename traits::template member_static_ptr<C>; + + mfp_helper(); + + template<typename O, typename F> + void bind(O *object, F *mftp); + + R call(Targs&&... args) const noexcept(true) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto* func = reinterpret_cast<const member_abi_function<mfp_generic_class> *>(&m_resolved); + return (*func)(m_obj, std::forward<Targs>(args)...); + } + + member_abi_function<mfp_generic_class> m_resolved; + mfp_generic_class *m_obj; + }; + + template <ppmf_type PMFINTERNAL, typename R, typename... Targs> + using pmfp_helper_select = std::conditional< + pmf_is_register_return_type<R>::value + || PMFINTERNAL != ppmf_type::INTERNAL_MSC || (PPMF_EXPERIMENTAL), + mfp_helper<PMFINTERNAL, R, Targs...>, mfp_helper<ppmf_type::PMF, R, Targs...>>; + + template<ppmf_type PMFINTERNAL, typename SIGNATURE> class pmfp_base; + + template<ppmf_type PMFINTERNAL, typename R, typename... Targs> + class pmfp_base<PMFINTERNAL, R (Targs...)> : public pmfp_helper_select<PMFINTERNAL, R, Targs...>::type + { + static_assert((compile_info::env::value != ci_env::NVCC) || (PMFINTERNAL != ppmf_type::GNUC_PMF_CONV), "GNUC_PMF_CONV not supported by nvcc"); public: - class generic_class; -#if defined (__INTEL_COMPILER) && defined (_M_X64) // needed for "Intel(R) C++ Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 14.0.2.176 Build 20140130" at least - using generic_function = int [((sizeof(void *) + 4 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)]; -#elif defined(_MSC_VER) // all other cases - for MSVC maximum size is one pointer, plus 3 ints; all other implementations seem to be smaller - using generic_function = int[((sizeof(void *) + 3 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)]; -#else - using generic_function = R (generic_class::*)(Targs...); -#endif + using helper = typename pmfp_helper_select<PMFINTERNAL, R, Targs...>::type; + + using traits = mfp_traits<R, Targs...>; + pmfp_base() + : helper() { - int *p = reinterpret_cast<int *>(&m_func); - int *e = p + sizeof(generic_function) / sizeof(int); - for (; p < e; p++) - *p = 0; } - template<typename MemberFunctionType, typename O> - void set_base(MemberFunctionType mftp, O *object) + template<typename O, typename P> + pmfp_base(typename traits::template specific_member_function<O> mftp, P *object) + : helper() { - using function_ptr = R (O::*)(Targs...); - function_ptr t = mftp; - *reinterpret_cast<function_ptr *>(&m_func) = t; + this->bind(static_cast<O*>(object), &mftp); } + + template<typename O, typename P> + pmfp_base(typename traits::template const_specific_member_function<O> mftp, P *object) + : helper() + { + this->bind(static_cast<O*>(object), &mftp); + } + + mfp_generic_class *object() const noexcept { return this->m_obj; } + template<typename O> - inline R call(O *obj, Targs... args) + void set(typename traits::template specific_member_function<O> mftp, O *object) { - using function_ptr = R (O::*)(Targs...); - function_ptr t = *reinterpret_cast<function_ptr *>(&m_func); - return (obj->*t)(std::forward<Targs>(args)...); + this->bind(object, &mftp); } - bool is_set() { -#if defined(_MSC_VER) || (defined (__INTEL_COMPILER) && defined (_M_X64)) - int *p = reinterpret_cast<int *>(&m_func); - int *e = p + sizeof(generic_function) / sizeof(int); - for (; p < e; p++) - if (*p != 0) - return true; - - return false; -#else - return m_func != nullptr; -#endif + + R operator()(Targs... args) const noexcept(true) + { + return this->call(std::forward<Targs>(args)...); } + + bool isnull() const noexcept { return this->m_resolved == nullptr; } + explicit operator bool() const noexcept { return !isnull(); } + bool has_object() const noexcept { return this->m_obj != nullptr; } + bool operator==(const pmfp_base &rhs) const { return this->m_resolved == rhs.m_resolved; } + bool operator!=(const pmfp_base &rhs) const { return !(*this == rhs); } + private: - generic_function m_func; -#if 0 && defined(_MSC_VER) - int dummy[4]; -#endif }; -#elif ((PPMF_TYPE == PPMF_TYPE_GNUC_PMF_CONV) || (PPMF_TYPE == PPMF_TYPE_INTERNAL)) - template<typename R, typename... Targs> - class pmfp_base + template<typename Signature> + using pmfp = pmfp_base<ppmf_internal_selector::value, Signature>; + + /// + /// \brief Class to support delegate late binding + /// + /// When constructing delegates in constructors AND the referenced function + /// is virtual, the vtable may not yet be fully constructed. In these cases + /// the following class allows to construct the delegate later. + /// + /// ``` + /// plib::late_pmfp<plib::pmfp<void, pstring>> a(&nld_7493::printer); + /// // Store the a object somewhere + /// + /// // After full construction ... + /// + /// auto delegate_obj = a(this); + /// delegate_obj(pstring("Hello World!")); + /// ``` + template<typename T> + class late_pmfp { public: - using generic_function = void (*)(); - pmfp_base() : m_func(nullptr) {} + using return_type = T; + using traits = typename return_type::traits; + using generic_member_function = typename traits::template specific_member_function<mfp_generic_class>; + using static_creator = return_type (*)(const generic_member_function *, mfp_generic_class *); - template<typename MemberFunctionType, typename O> - void set_base(MemberFunctionType mftp, O *object) - { - #if (PPMF_TYPE == PPMF_TYPE_INTERNAL) - using function_ptr = MEMBER_ABI R (*)(O *obj, Targs... args); - function_ptr func(nullptr); - plib::mfp::get_mfp(mftp, func, object); - m_func = reinterpret_cast<generic_function>(func); - #elif (PPMF_TYPE == PPMF_TYPE_GNUC_PMF_CONV) - R (O::* pFunc)(Targs...) = mftp; - m_func = reinterpret_cast<generic_function>((object->*pFunc)); - #endif - } template<typename O> - R call(O *obj, Targs... args) const + late_pmfp(typename traits::template specific_member_function<O> mftp); + + template<typename O> + return_type operator()(O *object) const { - using function_ptr = MEMBER_ABI R (*)(O *obj, Targs... args); - return (reinterpret_cast<function_ptr>(m_func))(obj, std::forward<Targs>(args)...); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return m_creator(&m_raw, reinterpret_cast<mfp_generic_class *>(object)); } - bool is_set() noexcept { return m_func != nullptr; } - generic_function get_function() const noexcept { return m_func; } + private: - generic_function m_func; + + template <typename O> + static return_type creator(const generic_member_function *raw, mfp_generic_class *obj); + + generic_member_function m_raw; + static_creator m_creator; }; -#endif - template<typename R, typename... Targs> - class pmfp : public pmfp_base<R, Targs...> + template<typename MemberFunctionType> + mfp_raw<ppmf_type::INTERNAL_ITANIUM>::mfp_raw(MemberFunctionType mftp) + : m_function(0), m_this_delta(0) { - public: - class generic_class; + static_assert(sizeof(*this) >= sizeof(MemberFunctionType), "size mismatch"); + *reinterpret_cast<MemberFunctionType *>(this) = mftp; // NOLINT + // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject) + } + + template<typename MemberFunctionType> + mfp_raw<ppmf_type::INTERNAL_ARM>::mfp_raw(MemberFunctionType mftp) + : m_function(0), m_this_delta(0) + { + static_assert(sizeof(*this) >= sizeof(MemberFunctionType), "size mismatch"); + *reinterpret_cast<MemberFunctionType *>(this) = mftp; // NOLINT + } - template <class C> - using MemberFunctionType = R (C::*)(Targs...); + template<typename MemberFunctionType> + mfp_raw<ppmf_type::INTERNAL_MSC>::mfp_raw(MemberFunctionType mftp) + : m_function(0), m_this_delta(0), m_vptr_index(0), m_vt_index(0), m_size(0) + { + static_assert(sizeof(*this) >= sizeof(MemberFunctionType), "size mismatch"); + *reinterpret_cast<MemberFunctionType *>(this) = mftp; // NOLINT + m_size = sizeof(mftp); //NOLINT + } + + template<ppmf_type PMFINTERNAL, typename R, typename... Targs> + mfp_helper<PMFINTERNAL, R, Targs...>::mfp_helper() + : m_obj(nullptr) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved); + std::fill(s, s + sizeof(m_resolved), 0); + } + + template<ppmf_type PMFINTERNAL, typename R, typename... Targs> + template<typename O, typename F> + void mfp_helper<PMFINTERNAL, R, Targs...>::bind(O *object, F *mftp) + { + typename traits::template specific_member_function<O> pFunc; + static_assert(sizeof(pFunc) >= sizeof(F), "size error"); + //# NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + //# *reinterpret_cast<F *>(&pFunc) = *mftp; + reinterpret_copy(*mftp, pFunc); + raw_type mfpo(pFunc); + generic_function_storage rfunc(nullptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *robject = reinterpret_cast<mfp_generic_class *>(object); + mfpo.convert_to_generic(rfunc, robject); + reinterpret_copy(rfunc, this->m_resolved); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_obj = reinterpret_cast<mfp_generic_class *>(robject); + } - pmfp() : pmfp_base<R, Targs...>(), m_obj(nullptr) {} + template<typename R, typename... Targs> + mfp_helper<ppmf_type::PMF, R, Targs...>::mfp_helper() + : m_obj(nullptr) + , m_stub(nullptr) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved); + std::fill(s, s + sizeof(m_resolved), 0); + } - template<typename O> - pmfp(MemberFunctionType<O> mftp, O *object) - : pmfp_base<R, Targs...>() - { - this->set_base(mftp, object); - m_obj = reinterpret_cast<generic_class *>(object); - } + template<typename R, typename... Targs> + template<typename O, typename F> + void mfp_helper<ppmf_type::PMF, R, Targs...>::bind(O *object, F *mftp) + { + reinterpret_copy(*mftp, this->m_resolved); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_obj = reinterpret_cast<mfp_generic_class *>(object); + m_stub = &stub<O>; + } - template<typename O> - void set(MemberFunctionType<O> mftp, O *object) - { - this->set_base(mftp, object); - m_obj = reinterpret_cast<generic_class *>(object); - } + template<typename R, typename... Targs> + template<typename O> + R mfp_helper<ppmf_type::PMF, R, Targs...>::stub(const generic_member_function* funci, mfp_generic_class* obji, Targs&&... args) noexcept(true) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto* obj = reinterpret_cast<O*>(obji); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto* func = reinterpret_cast<const member_abi_function<O> *>(funci); + return (obj->*(*func))(std::forward<Targs>(args)...); + } - inline R operator()(Targs ... args) - { - return this->call(m_obj, std::forward<Targs>(args)...); - } + template<typename R, typename... Targs> + mfp_helper<ppmf_type::GNUC_PMF_CONV, R, Targs...>::mfp_helper() + : m_obj(nullptr) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved); + std::fill(s, s + sizeof(m_resolved), 0); + } - generic_class *object() const noexcept { return m_obj; } - bool has_object() const noexcept { return m_obj != nullptr; } - private: - generic_class *m_obj; - }; + template<typename R, typename... Targs> + template<typename O, typename F> + void mfp_helper<ppmf_type::GNUC_PMF_CONV, R, Targs...>::bind(O *object, F *mftp) + { + // nvcc still tries to compile the code below - even when shielded with a `if constexpr` +#if !defined(__NVCC__) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + member_abi_function<O> t = reinterpret_cast<member_abi_function<O>>(object->*(*mftp)); + reinterpret_copy(t, this->m_resolved); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_obj = reinterpret_cast<mfp_generic_class *>(object); +#endif + } + template<typename T> + template<typename O> + late_pmfp<T>::late_pmfp(typename traits::template specific_member_function<O> mftp) + : m_creator(creator<O>) + { + static_assert(sizeof(m_raw) >= sizeof(typename traits::template specific_member_function<O>), "size issue"); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + *reinterpret_cast<typename traits::template specific_member_function<O> *>(&m_raw) = mftp; + } + + template<typename T> + template<typename O> + typename late_pmfp<T>::return_type late_pmfp<T>::creator(const typename late_pmfp<T>::generic_member_function *raw, mfp_generic_class *obj) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto p = reinterpret_cast<const typename late_pmfp<T>::traits::template specific_member_function<O> *>(raw); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *o = reinterpret_cast<O *>(obj); + return return_type(*p, o); + } } // namespace plib -#endif /* PPMF_H_ */ +#endif +#endif // PPMF_H_ |