From 74728bc4d42ba21de9a58ad0442a413575e7e357 Mon Sep 17 00:00:00 2001 From: couriersud Date: Sun, 15 May 2022 12:03:54 +0200 Subject: [PATCH] netlist: Improved PPMF target support and test coverage. (#9752) This addresses most of the issues described in #8590. * Fixed standalone Visual Studio 2019 builds, including support for clang toolchain. * Added static stub to PMF to support MSVC ABI. * Better aligned ppmf syntax with MAME's delegate syntax. * Add tests/test_ppmf*.cpp testing examples given in #8590. Also worked around issues some versions of Apple clang have with 5aaae19230bd39dda6766641672f57276a3f9ea1. --- src/lib/netlist/.gitignore | 6 + src/lib/netlist/build/makefile | 9 + src/lib/netlist/buildVS/netlistlib.vcxproj | 6 + src/lib/netlist/buildVS/nltool.vcxproj | 5 + .../netlist/buildVS/nltool.vcxproj.filters | 9 + src/lib/netlist/buildVS/nlwav.vcxproj | 2 + src/lib/netlist/core/core_device.h | 4 +- src/lib/netlist/core/queue.h | 4 +- src/lib/netlist/nl_base.cpp | 4 +- src/lib/netlist/nl_interface.h | 2 +- src/lib/netlist/nl_setup.cpp | 2 +- src/lib/netlist/nltypes.h | 6 +- src/lib/netlist/plib/pfmtlog.h | 2 +- src/lib/netlist/plib/pmatrix2d.h | 2 +- src/lib/netlist/plib/ppmf.h | 596 +++++++++--------- src/lib/netlist/plib/ptypes.h | 55 +- src/lib/netlist/prg/nlwav.cpp | 8 +- src/lib/netlist/tests/test_pmfp.cpp | 71 +++ src/lib/netlist/tests/test_pmfp_multibase.cpp | 360 +++++++++++ 19 files changed, 834 insertions(+), 319 deletions(-) create mode 100755 src/lib/netlist/tests/test_pmfp.cpp create mode 100644 src/lib/netlist/tests/test_pmfp_multibase.cpp diff --git a/src/lib/netlist/.gitignore b/src/lib/netlist/.gitignore index b5665c29b92..75277061721 100644 --- a/src/lib/netlist/.gitignore +++ b/src/lib/netlist/.gitignore @@ -7,6 +7,12 @@ buildVS/x64/* buildVS/.vs/* nltool nlwav +nltool.html +nltool.js +nltool.wasm +nlwav.html +nlwav.js +nlwav.wasm # Windows executables *.exe diff --git a/src/lib/netlist/build/makefile b/src/lib/netlist/build/makefile index f011532a114..eb7f3b133fc 100644 --- a/src/lib/netlist/build/makefile +++ b/src/lib/netlist/build/makefile @@ -53,6 +53,9 @@ TIDY_FLAGSX += -modernize-use-nodiscard, #TIDY_FLAGSX += -cppcoreguidelines-prefer-member-initializer, #TIDY_FLAGSX += -modernize-use-transparent-functors, TIDY_FLAGSX += -readability-function-cognitive-complexity, +TIDY_FLAGSX += -readability-uppercase-literal-suffix + + #TIDY_FLAGSX += -cppcoreguidelines-avoid-non-const-global-variables space := @@ -271,6 +274,9 @@ native: gcc9: $(MAKE) CC=g++-9 LD=g++-9 CEXTRAFLAGS="-march=native -Wall -pedantic -Wpedantic -fext-numeric-literals -Wsign-compare -Wextra" EXTRALIBS="-lquadmath" +emsdk: + emmake $(MAKE) CC=emcc LD=emcc CEXTRAFLAGS="-fexceptions" LDEXTRAFLAGS="-fexceptions --emrun" OBJ=obj/emsdk EXESUFFIX=.html + clang: #$(MAKE) CC=clang++-11 LD=clang++-11 OBJ=obj/clang CEXTRAFLAGS="-march=native -msse4.2 -Weverything -Wall -pedantic -Wpedantic -Wunused-private-field -Wno-padded -Wno-unused-template -Wno-missing-variable-declarations -Wno-float-equal -Wconversion -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-format-nonliteral -Wno-exit-time-destructors" $(MAKE) CC=clang++-13 LD=clang++-13 OBJ=obj/clang CEXTRAFLAGS="-march=native \ @@ -281,6 +287,8 @@ clang: -Wmissing-variable-declarations -Wno-float-equal -Wconversion \ -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wformat-nonliteral \ -Wno-exit-time-destructors -Winconsistent-missing-destructor-override \ + -Wno-embedded-directive \ + -Wno-undefined-reinterpret-cast \ -Wunreachable-code \ -Wmissing-prototypes" @@ -295,6 +303,7 @@ clang-libc: -Wmissing-variable-declarations -Wno-float-equal -Wconversion \ -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wformat-nonliteral \ -Wno-exit-time-destructors -Winconsistent-missing-destructor-override \ + -Wno-undefined-reinterpret-cast \ -Wunreachable-code \ -Wmissing-prototypes" \ LDEXTRAFLAGS=-stdlib=libc++ diff --git a/src/lib/netlist/buildVS/netlistlib.vcxproj b/src/lib/netlist/buildVS/netlistlib.vcxproj index 1344d71ba50..588502552b0 100755 --- a/src/lib/netlist/buildVS/netlistlib.vcxproj +++ b/src/lib/netlist/buildVS/netlistlib.vcxproj @@ -104,6 +104,12 @@ Full Speed /bigobj + stdcpp17 + + + + + stdcpp17 diff --git a/src/lib/netlist/buildVS/nltool.vcxproj b/src/lib/netlist/buildVS/nltool.vcxproj index 7c6982e680d..ab4233430ba 100755 --- a/src/lib/netlist/buildVS/nltool.vcxproj +++ b/src/lib/netlist/buildVS/nltool.vcxproj @@ -102,6 +102,7 @@ Level3 Disabled _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + stdcpp17 Console @@ -136,6 +137,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\;$(SolutionDir)..\ + stdcpp17 Console @@ -150,6 +152,9 @@ /bigobj + + + diff --git a/src/lib/netlist/buildVS/nltool.vcxproj.filters b/src/lib/netlist/buildVS/nltool.vcxproj.filters index b7ef3607716..52f522f23e8 100755 --- a/src/lib/netlist/buildVS/nltool.vcxproj.filters +++ b/src/lib/netlist/buildVS/nltool.vcxproj.filters @@ -21,5 +21,14 @@ Source Files + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/src/lib/netlist/buildVS/nlwav.vcxproj b/src/lib/netlist/buildVS/nlwav.vcxproj index 3cadf366172..7471dc0dc1e 100755 --- a/src/lib/netlist/buildVS/nlwav.vcxproj +++ b/src/lib/netlist/buildVS/nlwav.vcxproj @@ -102,6 +102,7 @@ NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true $(SolutionDir)..\..\;$(SolutionDir)..\ + stdcpp17 Console @@ -132,6 +133,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true + stdcpp17 Console diff --git a/src/lib/netlist/core/core_device.h b/src/lib/netlist/core/core_device.h index d21da4c8b53..b9d09e14aca 100644 --- a/src/lib/netlist/core/core_device.h +++ b/src/lib/netlist/core/core_device.h @@ -48,7 +48,7 @@ namespace netlist { gsl_Expects(m_active_outputs >= 1); - if (m_activate && m_hint_deactivate) + if (!m_activate.isnull() && m_hint_deactivate) if (--m_active_outputs == 0) { m_activate(false); //dec_active(); @@ -78,7 +78,7 @@ namespace netlist } protected: - using activate_delegate = plib::pmfp; + using activate_delegate = plib::pmfp; activate_delegate m_activate; diff --git a/src/lib/netlist/core/queue.h b/src/lib/netlist/core/queue.h index 488a4a6f057..6286f407fc5 100644 --- a/src/lib/netlist/core/queue.h +++ b/src/lib/netlist/core/queue.h @@ -48,8 +48,8 @@ namespace netlist::detail public: using entry_t = plib::pqentry_t; using base_queue = timed_queue; - using id_delegate = plib::pmfp; - using obj_delegate = plib::pmfp; + using id_delegate = plib::pmfp; + using obj_delegate = plib::pmfp; explicit queue_base(std::size_t size, id_delegate get_id, obj_delegate get_obj) : timed_queue, false>(size) diff --git a/src/lib/netlist/nl_base.cpp b/src/lib/netlist/nl_base.cpp index 3d422dc4044..e349fb77746 100644 --- a/src/lib/netlist/nl_base.cpp +++ b/src/lib/netlist/nl_base.cpp @@ -209,8 +209,6 @@ namespace netlist ENTRY(PHAS_OPENMP) ENTRY(PUSE_OPENMP) ENTRY(PUSE_FLOAT128) - ENTRY(PPMF_TYPE) - ENTRY(PHAS_PMF_INTERNAL) ENTRY_EX(config::use_mempool::value) ENTRY_EX(config::use_queue_stats::value) ENTRY(NL_USE_COPY_INSTEAD_OF_REFERENCE) @@ -258,7 +256,7 @@ namespace netlist ENTRY_EX(sizeof(plib::plog_level)) ENTRY_EX(sizeof(nldelegate)) - ENTRY(PPMF_TYPE) + ENTRY(PPMF_FORCE_TYPE) ENTRY(PHAS_PMF_INTERNAL) #undef ENTRY diff --git a/src/lib/netlist/nl_interface.h b/src/lib/netlist/nl_interface.h index 40506a95587..c601b6a41fe 100644 --- a/src/lib/netlist/nl_interface.h +++ b/src/lib/netlist/nl_interface.h @@ -206,7 +206,7 @@ namespace netlist std::size_t id() const { return m_id; } private: - using setter_t = plib::pmfp; + using setter_t = plib::pmfp; template void setter(nl_fptype v) diff --git a/src/lib/netlist/nl_setup.cpp b/src/lib/netlist/nl_setup.cpp index 311251ec6d6..6ccc5fdc86a 100644 --- a/src/lib/netlist/nl_setup.cpp +++ b/src/lib/netlist/nl_setup.cpp @@ -1685,7 +1685,7 @@ void setup_t::prepare_to_run() for (auto &n : m_nlstate.nets()) { for (auto & term : m_nlstate.core_terms(*n)) - if (!term->delegate()) + if (term->delegate().isnull()) { log().fatal(MF_DELEGATE_NOT_SET_1(term->name())); throw nl_exception(MF_DELEGATE_NOT_SET_1(term->name())); diff --git a/src/lib/netlist/nltypes.h b/src/lib/netlist/nltypes.h index 41a93aa1efa..6d6fd2eb530 100644 --- a/src/lib/netlist/nltypes.h +++ b/src/lib/netlist/nltypes.h @@ -166,9 +166,9 @@ namespace netlist /// \brief Delegate type for device notification. /// - using nldelegate = plib::pmfp; - using nldelegate_ts = plib::pmfp; - using nldelegate_dyn = plib::pmfp; + using nldelegate = plib::pmfp; + using nldelegate_ts = plib::pmfp; + using nldelegate_dyn = plib::pmfp; namespace detail { diff --git a/src/lib/netlist/plib/pfmtlog.h b/src/lib/netlist/plib/pfmtlog.h index c2b364b247b..e27e66969e5 100644 --- a/src/lib/netlist/plib/pfmtlog.h +++ b/src/lib/netlist/plib/pfmtlog.h @@ -412,7 +412,7 @@ namespace plib { }; - using plog_delegate = plib::pmfp; + using plog_delegate = plib::pmfp; template class plog_channel : public pfmt_writer_t, build_enabled> diff --git a/src/lib/netlist/plib/pmatrix2d.h b/src/lib/netlist/plib/pmatrix2d.h index b85352db276..3874c146bb0 100644 --- a/src/lib/netlist/plib/pmatrix2d.h +++ b/src/lib/netlist/plib/pmatrix2d.h @@ -163,7 +163,7 @@ namespace plib pmatrix2d_vrl(const pmatrix2d_vrl &) = default; pmatrix2d_vrl &operator=(const pmatrix2d_vrl &) = default; pmatrix2d_vrl(pmatrix2d_vrl &&) noexcept = default; - pmatrix2d_vrl &operator=(pmatrix2d_vrl &&) noexcept = default; + pmatrix2d_vrl &operator=(pmatrix2d_vrl &&) noexcept(!compile_info::clang_apple_noexcept_issue::value) = default; ~pmatrix2d_vrl() = default; diff --git a/src/lib/netlist/plib/ppmf.h b/src/lib/netlist/plib/ppmf.h index 25e88a22eec..982ef573e70 100644 --- a/src/lib/netlist/plib/ppmf.h +++ b/src/lib/netlist/plib/ppmf.h @@ -8,7 +8,7 @@ /// \file ppmf.h /// /// -/// PMF_TYPE_GNUC_PMF +/// PMF_TYPE_PMF /// Use standard pointer to member function syntax C++11 /// /// PMF_TYPE_GNUC_PMF_CONV @@ -16,20 +16,16 @@ /// This is not standard compliant and needs /// -Wno-pmf-conversions to compile. /// -/// PMF_TYPE_INTERNAL +/// 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 +/// Benchmarks for ./nltool -c run -t 10 -n pongf src/mame/machine/nl_pongf.cpp /// -/// PMF_TYPE_INTERNAL: 215% 215% -/// PMF_TYPE_GNUC_PMF: 163% 196% -/// PMF_TYPE_GNUC_PMF_CONV: 215% 215% +/// PMF_TYPE_INTERNAL: 215% 215% 564% 580% +/// PMF_TYPE_GNUC_PMF: 163% 196% 516% 490% +/// PMF_TYPE_GNUC_PMF_CONV: 215% 215% 560% 575% /// -/// 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. /// #include "pconfig.h" @@ -37,67 +33,29 @@ #include #include // uintptr_t +#include #include //============================================================ // Macro magic //============================================================ -//#define PPMF_TYPE 2 +//#define PPMF_FORCE_TYPE 0 -#define PPMF_TYPE_PMF 0 -#define PPMF_TYPE_GNUC_PMF_CONV 1 -#define PPMF_TYPE_INTERNAL 2 - -// FIXME: Remove this macro madmess latest after September, 2020 -// FIXME: Do we still need to support MINGW <= 4.6? - -#if defined(__clang__) && defined(__i386__) && defined(_WIN32) - #define PHAS_PMF_INTERNAL 0 -#elif defined(__GNUC__) || defined(__clang__) - // does not work in versions over 4.7.x of 32bit MINGW - #if defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))) - #define PHAS_PMF_INTERNAL 0 - #elif defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__) - #define PHAS_PMF_INTERNAL 1 - #define MEMBER_ABI _thiscall - #elif defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64) || defined(__EMSCRIPTEN__) - #define PHAS_PMF_INTERNAL 2 - #else - #define PHAS_PMF_INTERNAL 1 - #endif -#elif defined(_MSC_VER) && defined (_M_X64) - #define PHAS_PMF_INTERNAL 3 -#else - #define PHAS_PMF_INTERNAL 0 -#endif - -#ifndef MEMBER_ABI - #define MEMBER_ABI -#endif - -#ifndef PPMF_TYPE - #if (PHAS_PMF_INTERNAL > 0) - #define PPMF_TYPE PPMF_TYPE_INTERNAL - #else - #define PPMF_TYPE PPMF_TYPE_PMF - #endif -#else - #if (PPMF_TYPE == PPMF_TYPE_INTERNAL) - #if (PHAS_PMF_INTERNAL == 0) - #error "Internal type not supported" - #endif - #else - #undef PHAS_PMF_INTERNAL - #define PHAS_PMF_INTERNAL 0 - #undef MEMBER_ABI - #define MEMBER_ABI - #endif -#endif +#define PPMF_TYPE_PMF 0 +#define PPMF_TYPE_GNUC_PMF_CONV 1 +#define PPMF_TYPE_INTERNAL_ITANIUM 2 +#define PPMF_TYPE_INTERNAL_ARM 3 +#define PPMF_TYPE_INTERNAL_MSC 4 #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpmf-conversions" +#pragma GCC diagnostic ignored "-Wpedantic" +#endif + +#ifndef PPMF_FORCE_TYPE +#define PPMF_FORCE_TYPE -1 #endif namespace plib { @@ -106,28 +64,34 @@ namespace plib { { using ci = compile_info; enum { value = + (PPMF_FORCE_TYPE >= 0) ? PPMF_FORCE_TYPE : (ci::type() == ci_compiler::CLANG && !ci::m64() - && ci::os() == ci_os::WINDOWS) ? 0 : - (ci::mingw() && !ci::m64() && ci::version() >= 407) ? 0 : - (ci::mingw() && !ci::m64()) ? 1 : + && ci::os() == ci_os::WINDOWS) ? PPMF_TYPE_PMF : + (ci::mingw() && !ci::m64() && ci::version() >= 407) ? PPMF_TYPE_PMF : + (ci::mingw() && !ci::m64()) ? PPMF_TYPE_PMF : // Dropped support for mingw32 < 407 PPMF_TYPE_INTERNAL_ITANIUM : ((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)) ? 2 : - (ci::type() == ci_compiler::CLANG || ci::type() == ci_compiler::GCC) ? 1 : - (ci::type() == ci_compiler::MSC && ci::m64()) ? 3 : - 0 + || ci::os() == ci_os::EMSCRIPTEN)) ? PPMF_TYPE_INTERNAL_ARM : + (ci::type() == ci_compiler::MSC && ci::m64()) ? PPMF_TYPE_INTERNAL_MSC : + (ci::type() == ci_compiler::CLANG || ci::type() == ci_compiler::GCC) ? PPMF_TYPE_INTERNAL_ITANIUM : + PPMF_TYPE_PMF }; }; - // FIXME: on supported platforms we should consider using GNU PMF extensions - // if no internal solution exists + static_assert(!(compile_info::type() == ci_compiler::CLANG && (PPMF_FORCE_TYPE) == (PPMF_TYPE_GNUC_PMF_CONV)), "clang does not support PPMF_TYPE_GNUC_PMF_CONV"); + static_assert(!(compile_info::type() == ci_compiler::NVCC && (PPMF_FORCE_TYPE) == (PPMF_TYPE_GNUC_PMF_CONV)), "nvcc does not support PPMF_TYPE_GNUC_PMF_CONV"); - using ppmf_type = std::integral_constant 0 ? PPMF_TYPE_INTERNAL : PPMF_TYPE_PMF)>; + template + struct mfp_traits + { + template using specific_member_function = R (C::*)(Targs...); + template using const_specific_member_function = R (C::*)(Targs...) const; + template using member_static_ref = R (*)(C &, Targs...); + template using member_static_ptr = R (*)(C *, Targs...); + }; - // check against previous implementation - static_assert(ppmf_internal::value == PHAS_PMF_INTERNAL, "internal mismatch"); - static_assert(ppmf_type::value == PPMF_TYPE, "type mismatch"); + class mfp_generic_class; /// /// \brief Used to derive a pointer to a member function. @@ -135,16 +99,18 @@ namespace plib { /// The following class was derived from the MAME delegate.h code. /// template - class mfp_raw + class mfp_raw; + + template <> + class mfp_raw { public: // construct from any member function pointer - class generic_class; using generic_function = void (*)(); template mfp_raw(MemberFunctionType mftp) - : m_function(0), m_this_delta(0), m_dummy1(0), m_dummy2(0), m_size(sizeof(mfp_raw)) + : m_function(0), m_this_delta(0) { static_assert(sizeof(*this) >= sizeof(MemberFunctionType), "size mismatch"); *reinterpret_cast(this) = mftp; // NOLINT @@ -153,60 +119,72 @@ namespace plib { //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 { - if (PMFINTERNAL == 1) - { - // apply the "this" delta to the object first - // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult,cppcoreguidelines-pro-type-reinterpret-cast) - generic_class *o_p_delta = reinterpret_cast(reinterpret_cast(object) + m_this_delta); + // apply the "this" delta to the object first + // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult,cppcoreguidelines-pro-type-reinterpret-cast) + auto *o_p_delta = reinterpret_cast(reinterpret_cast(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) == 0) - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - func = reinterpret_cast(m_function); - } - else - { - // otherwise, it is the byte index into the vtable where the actual function lives - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - std::uint8_t *vtable_base = *reinterpret_cast(o_p_delta); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - func = *reinterpret_cast(vtable_base + m_function - 1); - } - object = o_p_delta; - } - else if (PMFINTERNAL == 2) + // if the low bit of the vtable index is clear, then it is just a raw function pointer + if ((m_function & 1) == 0) { - if ((m_this_delta & 1) == 0) - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - object = reinterpret_cast(reinterpret_cast(object) + m_this_delta); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - func = reinterpret_cast(m_function); - } - else - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - object = reinterpret_cast(reinterpret_cast(object)); - - // otherwise, it is the byte index into the vtable where the actual function lives - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - std::uint8_t *vtable_base = *reinterpret_cast(object); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - func = *reinterpret_cast(vtable_base + m_function + m_this_delta - 1); - } - } - else if (PMFINTERNAL == 3) - { - const int SINGLE_MEMFUNCPTR_SIZE = sizeof(void (generic_class::*)()); - - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + // NOLINTNEXTLINE(performance-no-int-to-ptr,cppcoreguidelines-pro-type-reinterpret-cast) func = reinterpret_cast(m_function); - if (m_size == SINGLE_MEMFUNCPTR_SIZE + sizeof(int)) - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - object = reinterpret_cast(reinterpret_cast(object) + m_this_delta); + } + else + { + // otherwise, it is the byte index into the vtable where the actual function lives + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + std::uint8_t *vtable_base = *reinterpret_cast(o_p_delta); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + func = *reinterpret_cast(vtable_base + m_function - 1); + } + object = o_p_delta; + } + + // 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 + ptrdiff_t m_this_delta; // delta to apply to the 'this' pointer + }; + + template <> + class mfp_raw + { + public: + // construct from any member function pointer + using generic_function = void (*)(); + + template + mfp_raw(MemberFunctionType mftp) + : m_function(0), m_this_delta(0) + { + static_assert(sizeof(*this) >= sizeof(MemberFunctionType), "size mismatch"); + *reinterpret_cast(this) = mftp; // NOLINT + } + + //private: + // extract the generic function and adjust the object pointer + void convert_to_generic(generic_function &func, mfp_generic_class *&object) const + { + if ((m_this_delta & 1) == 0) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + object = reinterpret_cast(reinterpret_cast(object) + (m_this_delta >> 1)); + // NOLINTNEXTLINE(performance-no-int-to-ptr,cppcoreguidelines-pro-type-reinterpret-cast) + func = reinterpret_cast(m_function); + } + else + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + object = reinterpret_cast(reinterpret_cast(object)); + + // otherwise, it is the byte index into the vtable where the actual function lives + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + std::uint8_t *vtable_base = *reinterpret_cast(object); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + func = *reinterpret_cast(vtable_base + m_function + m_this_delta - 1); } } @@ -214,167 +192,250 @@ namespace plib { 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; + ptrdiff_t m_this_delta; // delta to apply to the 'this' pointer }; - template + template <> + class mfp_raw + { + 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 + mfp_raw(MemberFunctionType mftp) + : m_function(0), m_this_delta(0), m_vptr_offs(0), m_vt_index(0), m_size(0) + { + static_assert(sizeof(*this) >= sizeof(MemberFunctionType), "size mismatch"); + *reinterpret_cast(this) = mftp; // NOLINT + m_size = sizeof(mftp); //NOLINT + } + + //private: + // extract the generic function and adjust the object pointer + void convert_to_generic(generic_function &func, mfp_generic_class *&object) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *byteptr = reinterpret_cast(object); + + // test for pointer to member function cast across virtual inheritance relationship + if ((sizeof(unknown_base_equiv) == m_size) && m_vt_index) + { + // add offset from "this" pointer to location of vptr, and add offset to virtual base from vtable + byteptr += m_vptr_offs; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + std::uint8_t const *const vptr = *reinterpret_cast(byteptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + byteptr += *reinterpret_cast(vptr + m_vt_index); + } + + // add "this" pointer displacement if present in the pointer to member function + if (sizeof(single_base_equiv) < m_size) + byteptr += m_this_delta; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + object = reinterpret_cast(byteptr); + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr) + auto const *funcx = reinterpret_cast(m_function); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr) + func = reinterpret_cast(std::uintptr_t(funcx)); + } + + // 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_vptr_offs; // only used for visual studio x64 + int m_vt_index; + unsigned m_size; + }; + + template struct mfp_helper { - static_assert(PMFTYPE == 2 && PMFINTERNAL >= 1 && PMFINTERNAL <=3, "Invalid PMF type"); + protected: + static_assert(PMFINTERNAL >= PPMF_TYPE_INTERNAL_ITANIUM && PMFINTERNAL <= PPMF_TYPE_INTERNAL_MSC, "Invalid PMF type"); + + using traits = mfp_traits; + using generic_member_function = typename traits::template specific_member_function; + using generic_member_abi_function = typename traits::template member_static_ptr; using raw_type = mfp_raw; using generic_function_storage = typename raw_type::generic_function; - using generic_class = typename raw_type::generic_class; - template - using specific_member_function = R (C::*)(Targs...); - - template - using const_specific_member_function = R (C::*)(Targs...) const; - - using generic_member_function = specific_member_function; - - template - using member_abi_function = MEMBER_ABI R (*)(C *obj, Targs... args); - - template - static std::pair get(MemberFunctionType mftp, ObjectType *object) + mfp_helper() + : m_obj(nullptr) { - raw_type mfpo(mftp); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *s = reinterpret_cast(&m_resolved); + std::fill(s, s + sizeof(m_resolved), 0); + } + + template + void bind(O *object, F *mftp) + { + typename traits::template specific_member_function pFunc; + static_assert(sizeof(pFunc) >= sizeof(F), "size error"); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + //*reinterpret_cast(&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(object); + auto *robject = reinterpret_cast(object); mfpo.convert_to_generic(rfunc, robject); + reinterpret_copy(rfunc, this->m_resolved); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - return std::make_pair(reinterpret_cast(rfunc), reinterpret_cast(robject)); + m_obj = reinterpret_cast(robject); } - template - static R call(const member_abi_function *func, O *obj, Targs&&... args) noexcept(true) + + R call(Targs&&... args) const noexcept(true) { - return (*func)(obj, std::forward(args)...); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const auto* func = reinterpret_cast(&m_resolved); + return (*func)(m_obj, std::forward(args)...); } + generic_function_storage m_resolved; + mfp_generic_class *m_obj; }; template - struct mfp_helper + struct mfp_helper { - template - using specific_member_function = R (C::*)(Targs...); + protected: + using traits = mfp_traits; + using generic_member_function = typename traits::template specific_member_function; template - using const_specific_member_function = R (C::*)(Targs...) const; + using member_abi_function = typename traits::template specific_member_function; - class generic_class; - using generic_member_function = specific_member_function; - - template - using member_abi_function = specific_member_function; - - using generic_function_storage = generic_member_function; - - template - static std::pair get(MemberFunctionType mftp, ObjectType *object) + mfp_helper() + : m_obj(nullptr) + , m_stub(nullptr) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - return std::make_pair(reinterpret_cast(mftp), reinterpret_cast(object)); + auto *s = reinterpret_cast(&m_resolved); + std::fill(s, s + sizeof(m_resolved), 0); } - template - static R call(const member_abi_function *func, O *obj, Targs&&... args) noexcept(true) + template + void bind(O *object, F *mftp) { + reinterpret_copy(*mftp, this->m_resolved); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_obj = reinterpret_cast(object); + m_stub = &stub; + } + + R call(Targs&&... args) const noexcept(true) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto* func = reinterpret_cast(&m_resolved); + return (*m_stub)(func, m_obj, std::forward(args)...); + } + + 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 + static R stub(const generic_member_function *funci, mfp_generic_class *obji, Targs&&... args) noexcept(true) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *obj = reinterpret_cast(obji); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *func = reinterpret_cast *>(funci); return (obj->*(*func))(std::forward(args)...); } }; +#if NVCCBUILD == 0 template - struct mfp_helper + struct mfp_helper { - template - using specific_member_function = R (C::*)(Targs...); + protected: + using traits = mfp_traits; template - using const_specific_member_function = R (C::*)(Targs...) const; + using member_abi_function = typename traits::template member_static_ptr; - class generic_class; - using generic_member_function = specific_member_function; - - template - using member_abi_function = MEMBER_ABI R (*)(C *obj, Targs... args); - - using generic_function_storage = void (*)(); - - template - static std::pair get(MemberFunctionType mftp, ObjectType *object) + mfp_helper() + : m_obj(nullptr) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - return std::make_pair(reinterpret_cast(object->*mftp), reinterpret_cast(object)); + auto *s = reinterpret_cast(&m_resolved); + std::fill(s, s + sizeof(m_resolved), 0); } - template - static R call(const member_abi_function *func, O *obj, Targs&&... args) noexcept(true) - { - return (*func)(obj, std::forward(args)...); - } - }; - template - class pmfp_base + template + void bind(O *object, F *mftp) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + member_abi_function t = reinterpret_cast>(object->*(*mftp)); + reinterpret_copy(t, this->m_resolved); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_obj = reinterpret_cast(object); + } + + R call(Targs&&... args) const noexcept(true) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto* func = reinterpret_cast *>(&m_resolved); + return (*func)(m_obj, std::forward(args)...); + } + + member_abi_function m_resolved; + mfp_generic_class *m_obj; + }; +#endif + + template + using pmfp_helper_select = std::conditional< + std::is_void_v || + std::is_scalar_v || + std::is_reference_v || PMFINTERNAL != PPMF_TYPE_INTERNAL_MSC, + mfp_helper, mfp_helper>; + + template class pmfp_base; + + template + class pmfp_base : public pmfp_helper_select::type { public: - using helper = mfp_helper; + using helper = typename pmfp_helper_select::type; - template - using specific_member_function = typename helper::template specific_member_function; - - template - using const_specific_member_function = typename helper::template const_specific_member_function; - - using generic_class = typename helper::generic_class; - using generic_member_function = typename helper::generic_member_function; - - template - using member_abi_function = typename helper::template member_abi_function; - - using generic_function_storage = typename helper::generic_function_storage; + using traits = mfp_traits; pmfp_base() - : m_obj(nullptr) + : helper() { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - auto *s = reinterpret_cast(&m_resolved); - std::fill(s, s + sizeof(m_resolved), 0); } - template - pmfp_base(specific_member_function mftp, O *object) - : m_obj(nullptr) + template + pmfp_base(typename traits::template specific_member_function mftp, P *object) + : helper() { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - auto *s = reinterpret_cast(&m_resolved); - std::fill(s, s + sizeof(m_resolved), 0); - bind>(object, &mftp); + this->bind(static_cast(object), &mftp); } - template - pmfp_base(const_specific_member_function mftp, O *object) - : m_obj(nullptr) + template + pmfp_base(typename traits::template const_specific_member_function mftp, P *object) + : helper() { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - auto *s = reinterpret_cast(&m_resolved); - std::fill(s, s + sizeof(m_resolved), 0); - bind>(object, &mftp); + this->bind(static_cast(object), &mftp); } - generic_class *object() const noexcept { return m_obj; } - bool has_object() const noexcept { return m_obj != nullptr; } + mfp_generic_class *object() const noexcept { return this->m_obj; } template - void set(specific_member_function mftp, O *object) + void set(typename traits::template specific_member_function mftp, O *object) { - bind>(object, &mftp); + this->bind(object, &mftp); } R operator()(Targs... args) const noexcept(true) @@ -382,48 +443,17 @@ namespace plib { return this->call(std::forward(args)...); } - operator bool() const noexcept { return m_resolved != nullptr; } + 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: - template - void bind(O * object, MF *fraw) - { - SPC pFunc; - static_assert(sizeof(pFunc) >= sizeof(MF), "size error"); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - *reinterpret_cast(&pFunc) = *fraw; - - auto r = helper::template get>(pFunc, object); - - static_assert(sizeof(m_resolved) >= sizeof(r.first), "size mismatch 1"); - static_assert(sizeof(m_resolved) >= sizeof(member_abi_function), "size mismatch 2"); - - //*reinterpret_cast *>(&m_resolved) = r.first; - reinterpret_copy(r.first, m_resolved); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - m_obj = reinterpret_cast(r.second); - } - - template - R call(O *obj, Targs&&... args) const noexcept(true) - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - return helper::call(reinterpret_cast *>(&m_resolved), - obj, std::forward(args)...); - } - - R call(Targs&&... args) const noexcept(true) - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - return helper::call(reinterpret_cast *>(&m_resolved), - m_obj, std::forward(args)...); - } - - generic_function_storage m_resolved; - generic_class *m_obj; }; - template - using pmfp = pmfp_base; + template + using pmfp = pmfp_base; /// /// \brief Class to support delegate late binding @@ -437,8 +467,8 @@ namespace plib { /// /// // After full construction ... /// - /// auto dele = a(this); - /// dele(pstring("Hello World!")); + /// auto delegate_obj = a(this); + /// delegate_obj(pstring("Hello World!")); /// template class late_pmfp @@ -446,41 +476,33 @@ namespace plib { public: using return_type = T; - - template - using specific_member_function = typename return_type::template specific_member_function; // noexcept(true) --> c++-17 - - using generic_member_function = typename return_type::generic_member_function; - - class generic_class; - - using static_creator = return_type (*)(const generic_member_function *, generic_class *); - - late_pmfp() = default; + using traits = typename return_type::traits; + using generic_member_function = typename traits::template specific_member_function; + using static_creator = return_type (*)(const generic_member_function *, mfp_generic_class *); template - late_pmfp(specific_member_function mftp) + late_pmfp(typename traits::template specific_member_function mftp) : m_creator(creator) { - static_assert(sizeof(m_raw) >= sizeof(specific_member_function), "size issue"); + static_assert(sizeof(m_raw) >= sizeof(typename traits::template specific_member_function), "size issue"); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - *reinterpret_cast *>(&m_raw) = mftp; + *reinterpret_cast *>(&m_raw) = mftp; } template return_type operator()(O *object) const { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - return m_creator(&m_raw, reinterpret_cast(object)); + return m_creator(&m_raw, reinterpret_cast(object)); } private: template - static return_type creator(const generic_member_function *raw, generic_class *obj) + static return_type creator(const generic_member_function *raw, mfp_generic_class *obj) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - auto p = reinterpret_cast *>(raw); + auto p = reinterpret_cast *>(raw); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) auto o = reinterpret_cast(obj); return return_type(*p, o); diff --git a/src/lib/netlist/plib/ptypes.h b/src/lib/netlist/plib/ptypes.h index b48b43039c1..2c5fe088a2b 100644 --- a/src/lib/netlist/plib/ptypes.h +++ b/src/lib/netlist/plib/ptypes.h @@ -22,7 +22,6 @@ #endif #endif -// noexcept on move operator -> issue with macosx clang #define PCOPYASSIGNMOVE(name, def) \ PCOPYASSIGN(name, def) \ PMOVEASSIGN(name, def) @@ -32,8 +31,8 @@ name &operator=(const name &) = def; #define PMOVEASSIGN(name, def) \ - name(name &&) /*noexcept*/ = def; \ - name &operator=(name &&) /*noexcept*/ = def; + name(name &&) noexcept = def; \ + name &operator=(name &&) noexcept = def; #if defined(EMSCRIPTEN) #undef EMSCRIPTEN @@ -82,7 +81,8 @@ namespace plib UNKNOWN, CLANG, GCC, - MSC + MSC, + NVCC }; enum class ci_os @@ -101,18 +101,19 @@ namespace plib UNKNOWN, X86, ARM, - MIPS + MIPS, + IA64 }; struct compile_info { #ifdef _WIN32 using win32 = std::integral_constant; - #ifdef UNICODE - using unicode = std::integral_constant; - #else - using unicode = std::integral_constant; - #endif + #ifdef UNICODE + using unicode = std::integral_constant; + #else + using unicode = std::integral_constant; + #endif #else using win32 = std::integral_constant; using unicode = std::integral_constant; @@ -130,15 +131,18 @@ namespace plib static constexpr int128_type int128_max() { return int128_type(); } static constexpr uint128_type uint128_max() { return uint128_type(); } #endif - #ifdef __clang__ + #if (NVCCBUILD > 0) + using type = std::integral_constant; + using version = std::integral_constant; + #elif defined(_MSC_VER) + using type = std::integral_constant; + using version = std::integral_constant; + #elif defined(__clang__) using type = std::integral_constant; using version = std::integral_constant; #elif defined(__GNUC__) using type = std::integral_constant; using version = std::integral_constant; - #elif defined(_MSC_VER) - using type = std::integral_constant; - using version = std::integral_constant; #else using type = std::integral_constant; using version = std::integral_constant; @@ -174,6 +178,8 @@ namespace plib using arch = std::integral_constant; #elif defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64) using arch = std::integral_constant; + #elif defined(__ia64__) + using arch = std::integral_constant; #else using arch = std::integral_constant; #endif @@ -182,6 +188,11 @@ namespace plib #else using mingw = std::integral_constant; #endif + #if defined(__APPLE__) + using clang_apple_noexcept_issue = std::integral_constant; + #else + using clang_apple_noexcept_issue = std::integral_constant; + #endif }; } // namespace plib @@ -314,6 +325,22 @@ namespace plib std::copy(sp, sp + sizeof(S), dp); } + /// \brief Test if type R has a stream operator << defined + /// + /// has_ostram_operator:: value should be true + /// + /// \tparam LEFT Stream type + /// \tparam RIGHT Type to check for operator overload + + template + struct has_ostream_operator_impl { + template static auto test(V*) -> decltype(std::declval() << std::declval()); + template static auto test(...) -> std::false_type; + //static constexpr const bool value = std::is_same(0))>::value; + using type = typename std::is_same(nullptr))>::type; + }; + template + struct has_ostream_operator : has_ostream_operator_impl::type {}; } // namespace plib diff --git a/src/lib/netlist/prg/nlwav.cpp b/src/lib/netlist/prg/nlwav.cpp index 9685e87bd97..05d524fa1e2 100644 --- a/src/lib/netlist/prg/nlwav.cpp +++ b/src/lib/netlist/prg/nlwav.cpp @@ -177,7 +177,7 @@ private: class log_processor { public: - using callback_type = plib::pmfp; + using callback_type = plib::pmfp; struct elem { @@ -256,7 +256,7 @@ private: struct aggregator { - using callback_type = plib::pmfp; + using callback_type = plib::pmfp; aggregator(std::size_t channels, double quantum, callback_type cb) : m_channels(channels) @@ -299,7 +299,7 @@ private: struct filter_hp { - using callback_type = plib::pmfp; + using callback_type = plib::pmfp; filter_hp(double freq, bool boost, std::size_t channels, callback_type cb) : m_cb(cb) @@ -335,7 +335,7 @@ private: struct filter_lp { - using callback_type = plib::pmfp; + using callback_type = plib::pmfp; filter_lp(double freq, std::size_t channels, callback_type cb) : m_cb(cb) diff --git a/src/lib/netlist/tests/test_pmfp.cpp b/src/lib/netlist/tests/test_pmfp.cpp new file mode 100755 index 00000000000..f8bdaa9c0ae --- /dev/null +++ b/src/lib/netlist/tests/test_pmfp.cpp @@ -0,0 +1,71 @@ +// license:BSD-3-Clause +// copyright-holders:Couriersud + +/// +/// \file test_pmfp.cpp +/// +/// tests for plib::pmfp +/// + +#include "plib/ptests.h" + +#include "plib/pexception.h" +#include "plib/ppmf.h" + +#include +#include + +/// plib::late_pmfp> a(&nld_7493::printer); +/// // Store the a object somewhere +/// +/// // After full construction ... +/// +/// auto dele = a(this); +/// dele(pstring("Hello World!")); + +class test_late_pmfp : public plib::testing::Test +{ +protected: + class Ta + { + public: + void ap(int &r) { r = ax; } + int ax; + }; + +}; + +PTEST_F(test_late_pmfp, late_pmfp) +{ + plib::late_pmfp> late(&Ta::ap); + + Ta a; + a.ax = 1; + auto mfp(late(&a)); + int r(0); + mfp(r); + PEXPECT_TRUE(r == 1); +} + +PTEST(test_compile, compile) +{ + plib::pmfp_base mfp_PPMF_TYPE_PMF; + plib::pmfp_base mfp_PPMF_TYPE_INTERNAL_ITANIUM; + plib::pmfp_base mfp_PPMF_TYPE_INTERNAL_ARM; + plib::pmfp_base mfp_PPMF_TYPE_INTERNAL_MSC; + + PEXPECT_TRUE(mfp_PPMF_TYPE_PMF.isnull()); + PEXPECT_TRUE(mfp_PPMF_TYPE_INTERNAL_ITANIUM.isnull()); + PEXPECT_TRUE(mfp_PPMF_TYPE_INTERNAL_ARM.isnull()); + PEXPECT_TRUE(mfp_PPMF_TYPE_INTERNAL_MSC.isnull()); + +#if defined(__GNUC__) && !defined(__clang__) && (NVCCBUILD == 0) + plib::pmfp_base mfp_PPMF_TYPE_GNUC_PMF_CONV; + PEXPECT_TRUE(mfp_PPMF_TYPE_GNUC_PMF_CONV.isnull()); +#else + PEXPECT_NE("PPMF_TYPE_GNUC_PMF_CONV not supported on this build", ""); +#endif +#if defined(__EMSCRIPTEN__) + PEXPECT_EQ(plib::ppmf_internal::value,PPMF_TYPE_INTERNAL_ARM); +#endif +} diff --git a/src/lib/netlist/tests/test_pmfp_multibase.cpp b/src/lib/netlist/tests/test_pmfp_multibase.cpp new file mode 100644 index 00000000000..20a1fc9aba8 --- /dev/null +++ b/src/lib/netlist/tests/test_pmfp_multibase.cpp @@ -0,0 +1,360 @@ +// license:BSD-3-Clause +// copyright-holders:Couriersud + +/// +/// \file test_pmfp_multibase.cpp +/// +/// tests for plib::pmfp +/// + +#include "plib/ptypes.h" +#include "plib/pexception.h" +#include "plib/ppmf.h" + +#include +#include +#include +#include +#include + +template +typename std::enable_if_t, std::pair>::value, std::basic_ostream&> +operator << (std::basic_ostream& os, const std::pair &p) +{ + os << "{ " << p.first << ", " << p.second << " }"; + return os; +} + +#include "plib/ptests.h" + +template +typename std::enable_if_t, T>::value, std::basic_ostream&> +operator << (std::basic_ostream& os, const T &p) +{ + plib::unused_var(p); + os << std::string(typeid(T).name()); + return os; +} + +class pmfp_test_complex_return : public plib::testing::Test +{ +protected: + using ret_t = std::pair; + + //using test_delegate1 = plib::pmfp; + using test_delegate = plib::pmfp; + + class a + { + public: + ret_t ap() { return ret_t(static_cast(this), ax); } + int ax; + }; + + class b + { + public: + ret_t bp() { return ret_t(static_cast(this), bx); } + int bx; + }; + + class multibase : public a, public b + { + public: + }; + + + class vtb + { + public: + virtual ~vtb() = default; + virtual ret_t vp1() { return ret_t(static_cast(this), 1); } + virtual ret_t vp2() { return ret_t(static_cast(this), 2); } + }; + + class vti1 : public vtb + { + public: + virtual ret_t vp1() override { return ret_t(static_cast(this), 3); } + virtual ret_t vp2() override { return ret_t(static_cast(this), 4); } + }; + + class vti2 : public a, public vtb + { + public: + virtual ret_t vp1() override { return ret_t(static_cast(this), 13); } + virtual ret_t vp2() override { return ret_t(static_cast(this), 14); } + }; + + class forward; + + test_delegate make_forward_delegate(ret_t (forward::*func)(), forward *obj) + { + return test_delegate(func, obj); + } + + class vb + { + public: + ret_t xp() { return ret_t(static_cast(this), x); } + int x=10; + }; + + class vd1 : public virtual vb + { + public: + ret_t yp() { return ret_t(static_cast(this), y); } + int y; + }; + + class vd2 : public virtual vb + { + public: + ret_t zp() { return ret_t(static_cast(this), z); } + int z; + }; + + class forward : public vd1, public vd2 + { + public: + }; + +}; + +class pmfp_test_simple_return : public plib::testing::Test +{ +protected: + using ret_t = std::pair; + + //using test_delegate1 = plib::pmfp; + using test_delegate = plib::pmfp; + + class a + { + public: + void ap(ret_t &r) { r = ret_t(static_cast(this), ax); } + int ax; + }; + + class b + { + public: + void bp(ret_t &r) { r = ret_t(static_cast(this), bx); } + int bx; + }; + + class multibase : public a, public b + { + public: + }; + + + class vtb + { + public: + virtual ~vtb() = default; + virtual void vp1(ret_t &r) { r = ret_t(static_cast(this), 1); } + virtual void vp2(ret_t &r) { r = ret_t(static_cast(this), 2); } + }; + + class vti1 : public vtb + { + public: + virtual void vp1(ret_t &r) override { r = ret_t(static_cast(this), 3); } + virtual void vp2(ret_t &r) override { r = ret_t(static_cast(this), 4); } + }; + + class vti2 : public a, public vtb + { + public: + virtual void vp1(ret_t &r) override { r = ret_t(static_cast(this), 13); } + virtual void vp2(ret_t &r) override { r = ret_t(static_cast(this), 14); } + }; + + class forward; + + test_delegate make_forward_delegate(void (forward::*func)(ret_t &), forward *obj) + { + return test_delegate(func, obj); + } + + class vb + { + public: + void xp(ret_t &r) { r = ret_t(static_cast(this), x); } + int x=10; + }; + + class vd1 : public virtual vb + { + public: + void yp(ret_t &r) { r = ret_t(static_cast(this), y); } + int y; + }; + + class vd2 : public virtual vb + { + public: + void zp(ret_t &r) { r = ret_t(static_cast(this), z); } + int z; + }; + + class forward : public vd1, public vd2 + { + public: + }; +}; + +PTEST_F(pmfp_test_complex_return, multibase_test) +{ + multibase obj; + obj.ax = 1; + obj.bx = 2; + test_delegate f(&multibase::ap, &obj); + test_delegate g(&multibase::bp, &obj); + + // a=0x63fbf8 + // b=0x63fbfc + // a=0x63fbf8(1) + // b=0x63fbfc(2) + // b=0x63fbfc(2) + // b=0x63fbfc(2) + PEXPECT_EQ(f(), ret_t(static_cast(static_cast(&obj)), 1)); + auto g_ret = g(); + PEXPECT_EQ(g_ret, ret_t(static_cast(static_cast(&obj)), 2)); + + test_delegate h = g; + PEXPECT_EQ(g_ret, h()); + f = g; + PEXPECT_EQ(g_ret, f()); + + //vti1=0xdf6fb0.vp1 + //vti1=0xdf6fb0.vp2 + + std::unique_ptr ptr; + ptr.reset(new vti1); + f = test_delegate(&vtb::vp1, ptr.get()); + auto f1_ret = f(); + f = test_delegate(&vtb::vp2, ptr.get()); + auto f2_ret = f(); + PEXPECT_EQ(f1_ret.first, f2_ret.first); + PEXPECT_EQ(f1_ret.second, 3); + PEXPECT_EQ(f2_ret.second, 4); + + // vti2=0xdf6d50.vp1 + // vti2=0xdf6d50.vp2 + ptr.reset(new vti2); + f = test_delegate(&vtb::vp1, ptr.get()); + f1_ret = f(); + f = test_delegate(&vtb::vp2, ptr.get()); + f2_ret = f(); + PEXPECT_EQ(f1_ret.first, f2_ret.first); + PEXPECT_EQ(f1_ret.second, 13); + PEXPECT_EQ(f2_ret.second, 14); + + //vb=0x63fafc + //vd1=0x63fae0 + //vd2=0x63faf0 + //vd1=0x63fae0(8) + //vd2=0x63faf0(9) + + forward obj2; + obj2.x = 7; + obj2.y = 8; + obj2.z = 9; + PEXPECT_NE(static_cast(static_cast(&obj2)), static_cast(static_cast(&obj2))); + PEXPECT_NE(static_cast(static_cast(&obj2)), static_cast(static_cast(&obj2))); + +#if defined(_MSC_VER) && !defined(__clang__) + f = make_forward_delegate(&forward::xp, &obj2); + PEXPECT_TRUE(f().second == 7); + std::cout << f().second << " " << obj2.xp().second << "\n"; +#endif + f = make_forward_delegate(&forward::yp, &obj2); + PEXPECT_EQ(f().second, 8); + f = make_forward_delegate(&forward::zp, &obj2); + PEXPECT_EQ(f().second, 9); +} + +PTEST_F(pmfp_test_simple_return, multibase_test) +{ + multibase obj; + obj.ax = 1; + obj.bx = 2; + test_delegate f(&multibase::ap, &obj); + test_delegate g(&multibase::bp, &obj); + + // a=0x63fbf8 + // b=0x63fbfc + // a=0x63fbf8(1) + // b=0x63fbfc(2) + // b=0x63fbfc(2) + // b=0x63fbfc(2) + ret_t fr; + f(fr); + PEXPECT_EQ(fr, ret_t(static_cast(static_cast(&obj)), 1)); + ret_t gr; + g(gr); + PEXPECT_EQ(gr, ret_t(static_cast(static_cast(&obj)), 2)); + //PEXPECT_EQ(f, g); + + test_delegate h = g; + ret_t hr; + h(hr); + PEXPECT_EQ(gr, hr); + f = g; + f(fr); + PEXPECT_EQ(gr, fr); + + //vti1=0xdf6fb0.vp1 + //vti1=0xdf6fb0.vp2 + + std::unique_ptr ptr; + ptr.reset(new vti1); + f = test_delegate(&vtb::vp1, ptr.get()); + ret_t f1_ret; + f(f1_ret); + f = test_delegate(&vtb::vp2, ptr.get()); + ret_t f2_ret; + f(f2_ret); + PEXPECT_EQ(f1_ret.first, f2_ret.first); + PEXPECT_EQ(f1_ret.second, 3); + PEXPECT_EQ(f2_ret.second, 4); + + // vti2=0xdf6d50.vp1 + // vti2=0xdf6d50.vp2 + ptr.reset(new vti2); + f = test_delegate(&vtb::vp1, ptr.get()); + f(f1_ret); + f = test_delegate(&vtb::vp2, ptr.get()); + f(f2_ret); + PEXPECT_EQ(f1_ret.first, f2_ret.first); + PEXPECT_EQ(f1_ret.second, 13); + PEXPECT_EQ(f2_ret.second, 14); + + //vb=0x63fafc + //vd1=0x63fae0 + //vd2=0x63faf0 + //vd1=0x63fae0(8) + //vd2=0x63faf0(9) + + forward obj2; + obj2.x = 7; + obj2.y = 8; + obj2.z = 9; + PEXPECT_NE(static_cast(static_cast(&obj2)), static_cast(static_cast(&obj2))); + PEXPECT_NE(static_cast(static_cast(&obj2)), static_cast(static_cast(&obj2))); + +#if defined(_MSC_VER) && !defined(__clang__) + f = make_forward_delegate(&forward::xp, &obj2); + f(fr); + PEXPECT_TRUE(fr.second == 7); + std::cout << fr.second << "\n"; +#endif + f = make_forward_delegate(&forward::yp, &obj2); + f(fr); + PEXPECT_EQ(fr.second, 8); + f = make_forward_delegate(&forward::zp, &obj2); + f(fr); + PEXPECT_EQ(fr.second, 9); +}