mirror of
https://github.com/holub/mame
synced 2025-04-21 07:52:35 +03:00
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 5aaae19230
.
This commit is contained in:
parent
3da1e6f2b7
commit
74728bc4d4
6
src/lib/netlist/.gitignore
vendored
6
src/lib/netlist/.gitignore
vendored
@ -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
|
||||
|
@ -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++
|
||||
|
@ -104,6 +104,12 @@
|
||||
<Optimization>Full</Optimization>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<AdditionalOptions>/bigobj</AdditionalOptions>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
@ -102,6 +102,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@ -136,6 +137,7 @@
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\..\;$(SolutionDir)..\</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@ -150,6 +152,9 @@
|
||||
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">/bigobj</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\tests\test_pfunction.cpp" />
|
||||
<ClCompile Include="..\tests\test_pmfp.cpp" />
|
||||
<ClCompile Include="..\tests\test_pmfp_multibase.cpp" />
|
||||
<ClCompile Include="..\tests\test_pstring.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="netlistlib.vcxproj">
|
||||
|
@ -21,5 +21,14 @@
|
||||
<ClCompile Include="..\tests\test_pfunction.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\tests\test_pmfp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\tests\test_pstring.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\tests\test_pmfp_multibase.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -102,6 +102,7 @@
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\..\;$(SolutionDir)..\</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@ -132,6 +133,7 @@
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
|
@ -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<void, bool>;
|
||||
using activate_delegate = plib::pmfp<void (bool)>;
|
||||
|
||||
activate_delegate m_activate;
|
||||
|
||||
|
@ -48,8 +48,8 @@ namespace netlist::detail
|
||||
public:
|
||||
using entry_t = plib::pqentry_t<netlist_time_ext, O *>;
|
||||
using base_queue = timed_queue<entry_t, false>;
|
||||
using id_delegate = plib::pmfp<std::size_t, const O *>;
|
||||
using obj_delegate = plib::pmfp<O *, std::size_t>;
|
||||
using id_delegate = plib::pmfp<std::size_t (const O *)>;
|
||||
using obj_delegate = plib::pmfp<O * (std::size_t)>;
|
||||
|
||||
explicit queue_base(std::size_t size, id_delegate get_id, obj_delegate get_obj)
|
||||
: timed_queue<plib::pqentry_t<netlist_time_ext, O *>, false>(size)
|
||||
|
@ -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
|
||||
|
@ -206,7 +206,7 @@ namespace netlist
|
||||
|
||||
std::size_t id() const { return m_id; }
|
||||
private:
|
||||
using setter_t = plib::pmfp<void,nl_fptype>;
|
||||
using setter_t = plib::pmfp<void (nl_fptype)>;
|
||||
|
||||
template <typename S>
|
||||
void setter(nl_fptype v)
|
||||
|
@ -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()));
|
||||
|
@ -166,9 +166,9 @@ namespace netlist
|
||||
|
||||
/// \brief Delegate type for device notification.
|
||||
///
|
||||
using nldelegate = plib::pmfp<void>;
|
||||
using nldelegate_ts = plib::pmfp<void, timestep_type, nl_fptype>;
|
||||
using nldelegate_dyn = plib::pmfp<void>;
|
||||
using nldelegate = plib::pmfp<void ()>;
|
||||
using nldelegate_ts = plib::pmfp<void (timestep_type, nl_fptype)>;
|
||||
using nldelegate_dyn = plib::pmfp<void ()>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
|
@ -412,7 +412,7 @@ namespace plib {
|
||||
|
||||
};
|
||||
|
||||
using plog_delegate = plib::pmfp<void, plog_level, const pstring &>;
|
||||
using plog_delegate = plib::pmfp<void (plog_level, const pstring &)>;
|
||||
|
||||
template <plog_level::E L, bool build_enabled = true>
|
||||
class plog_channel : public pfmt_writer_t<plog_channel<L, build_enabled>, build_enabled>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 <algorithm>
|
||||
#include <cstdint> // uintptr_t
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
//============================================================
|
||||
// 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<int, (ppmf_internal::value > 0 ? PPMF_TYPE_INTERNAL : PPMF_TYPE_PMF)>;
|
||||
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...);
|
||||
};
|
||||
|
||||
// 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 <int PMFINTERNAL>
|
||||
class mfp_raw
|
||||
class mfp_raw;
|
||||
|
||||
template <>
|
||||
class mfp_raw<PPMF_TYPE_INTERNAL_ITANIUM>
|
||||
{
|
||||
public:
|
||||
// construct from any member function pointer
|
||||
class generic_class;
|
||||
using generic_function = void (*)();
|
||||
|
||||
template<typename MemberFunctionType>
|
||||
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<MemberFunctionType *>(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<generic_class *>(reinterpret_cast<std::uint8_t *>(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<mfp_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) == 0)
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
func = reinterpret_cast<generic_function>(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<std::uint8_t **>(o_p_delta);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
func = *reinterpret_cast<generic_function *>(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<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
func = reinterpret_cast<generic_function>(m_function);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
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
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
func = *reinterpret_cast<generic_function *>(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<generic_function>(m_function);
|
||||
if (m_size == SINGLE_MEMFUNCPTR_SIZE + sizeof(int))
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
object = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(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<std::uint8_t **>(o_p_delta);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
func = *reinterpret_cast<generic_function *>(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<PPMF_TYPE_INTERNAL_ARM>
|
||||
{
|
||||
public:
|
||||
// construct from any member function pointer
|
||||
using generic_function = void (*)();
|
||||
|
||||
template<typename MemberFunctionType>
|
||||
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
|
||||
}
|
||||
|
||||
//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<mfp_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> 1));
|
||||
// NOLINTNEXTLINE(performance-no-int-to-ptr,cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
func = reinterpret_cast<generic_function>(m_function);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
object = reinterpret_cast<mfp_generic_class *>(reinterpret_cast<std::uint8_t *>(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<std::uint8_t **>(object);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
func = *reinterpret_cast<generic_function *>(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<int PMFTYPE, int PMFINTERNAL, typename R, typename... Targs>
|
||||
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)
|
||||
: 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<MemberFunctionType *>(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<std::uint8_t *>(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<std::uint8_t const *const *>(byteptr);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
byteptr += *reinterpret_cast<int const *>(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<mfp_generic_class *>(byteptr);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr)
|
||||
auto const *funcx = reinterpret_cast<std::uint8_t const *>(m_function);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr)
|
||||
func = reinterpret_cast<generic_function>(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<int PMFINTERNAL, typename R, typename... Targs>
|
||||
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<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;
|
||||
using generic_class = typename raw_type::generic_class;
|
||||
|
||||
template <class C>
|
||||
using specific_member_function = R (C::*)(Targs...);
|
||||
|
||||
template <class C>
|
||||
using const_specific_member_function = R (C::*)(Targs...) const;
|
||||
|
||||
using generic_member_function = specific_member_function<generic_class>;
|
||||
|
||||
template <class C>
|
||||
using member_abi_function = MEMBER_ABI R (*)(C *obj, Targs... args);
|
||||
|
||||
template<typename FunctionType, typename MemberFunctionType, typename ObjectType>
|
||||
static std::pair<FunctionType, ObjectType *> get(MemberFunctionType mftp, ObjectType *object)
|
||||
mfp_helper()
|
||||
: m_obj(nullptr)
|
||||
{
|
||||
raw_type mfpo(mftp);
|
||||
// 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, typename F>
|
||||
void 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<generic_class *>(object);
|
||||
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)
|
||||
return std::make_pair(reinterpret_cast<FunctionType>(rfunc), reinterpret_cast<ObjectType *>(robject));
|
||||
m_obj = reinterpret_cast<mfp_generic_class *>(robject);
|
||||
}
|
||||
template<typename O>
|
||||
static R call(const member_abi_function<O> *func, O *obj, Targs&&... args) noexcept(true)
|
||||
|
||||
R call(Targs&&... args) const noexcept(true)
|
||||
{
|
||||
return (*func)(obj, std::forward<Targs>(args)...);
|
||||
// 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)...);
|
||||
}
|
||||
generic_function_storage m_resolved;
|
||||
mfp_generic_class *m_obj;
|
||||
};
|
||||
|
||||
template<typename R, typename... Targs>
|
||||
struct mfp_helper<PPMF_TYPE_PMF, 0, R, Targs...>
|
||||
struct mfp_helper<PPMF_TYPE_PMF, R, Targs...>
|
||||
{
|
||||
template <class C>
|
||||
using specific_member_function = R (C::*)(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 const_specific_member_function = R (C::*)(Targs...) const;
|
||||
using member_abi_function = typename traits::template specific_member_function<C>;
|
||||
|
||||
class generic_class;
|
||||
using generic_member_function = specific_member_function<generic_class>;
|
||||
|
||||
template <class C>
|
||||
using member_abi_function = specific_member_function<C>;
|
||||
|
||||
using generic_function_storage = generic_member_function;
|
||||
|
||||
template<typename FunctionType, typename MemberFunctionType, typename ObjectType>
|
||||
static std::pair<FunctionType, ObjectType *> 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<FunctionType>(mftp), reinterpret_cast<ObjectType *>(object));
|
||||
auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved);
|
||||
std::fill(s, s + sizeof(m_resolved), 0);
|
||||
}
|
||||
|
||||
template<typename O>
|
||||
static R call(const member_abi_function<O> *func, O *obj, Targs&&... args) noexcept(true)
|
||||
template<typename O, typename F>
|
||||
void 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>;
|
||||
}
|
||||
|
||||
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)...);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// 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)...);
|
||||
}
|
||||
};
|
||||
|
||||
#if NVCCBUILD == 0
|
||||
template<typename R, typename... Targs>
|
||||
struct mfp_helper<PPMF_TYPE_GNUC_PMF_CONV, 0, R, Targs...>
|
||||
struct mfp_helper<PPMF_TYPE_GNUC_PMF_CONV, R, Targs...>
|
||||
{
|
||||
template <class C>
|
||||
using specific_member_function = R (C::*)(Targs...);
|
||||
protected:
|
||||
using traits = mfp_traits<R, Targs...>;
|
||||
|
||||
template <class C>
|
||||
using const_specific_member_function = R (C::*)(Targs...) const;
|
||||
using member_abi_function = typename traits::template member_static_ptr<C>;
|
||||
|
||||
class generic_class;
|
||||
using generic_member_function = specific_member_function<generic_class>;
|
||||
|
||||
template <class C>
|
||||
using member_abi_function = MEMBER_ABI R (*)(C *obj, Targs... args);
|
||||
|
||||
using generic_function_storage = void (*)();
|
||||
|
||||
template<typename FunctionType, typename MemberFunctionType, typename ObjectType>
|
||||
static std::pair<FunctionType, ObjectType *> get(MemberFunctionType mftp, ObjectType *object)
|
||||
mfp_helper()
|
||||
: m_obj(nullptr)
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
return std::make_pair(reinterpret_cast<FunctionType>(object->*mftp), reinterpret_cast<ObjectType *>(object));
|
||||
auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved);
|
||||
std::fill(s, s + sizeof(m_resolved), 0);
|
||||
}
|
||||
template<typename O>
|
||||
static R call(const member_abi_function<O> *func, O *obj, Targs&&... args) noexcept(true)
|
||||
{
|
||||
return (*func)(obj, std::forward<Targs>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<int PMFTYPE, int PMFINTERNAL, typename R, typename... Targs>
|
||||
class pmfp_base
|
||||
template<typename O, typename F>
|
||||
void bind(O *object, F *mftp)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
#endif
|
||||
|
||||
template <int PMFINTERNAL, typename R, typename... Targs>
|
||||
using pmfp_helper_select = std::conditional<
|
||||
std::is_void_v<R> ||
|
||||
std::is_scalar_v<R> ||
|
||||
std::is_reference_v<R> || PMFINTERNAL != PPMF_TYPE_INTERNAL_MSC,
|
||||
mfp_helper<PMFINTERNAL, R, Targs...>, mfp_helper<PPMF_TYPE_PMF, R, Targs...>>;
|
||||
|
||||
template<int PMFINTERNAL, typename SIGNATURE> class pmfp_base;
|
||||
|
||||
template<int PMFINTERNAL, typename R, typename... Targs>
|
||||
class pmfp_base<PMFINTERNAL, R (Targs...)> : public pmfp_helper_select<PMFINTERNAL, R, Targs...>::type
|
||||
{
|
||||
public:
|
||||
using helper = mfp_helper<PMFTYPE, PMFINTERNAL, R, Targs...>;
|
||||
using helper = typename pmfp_helper_select<PMFINTERNAL, R, Targs...>::type;
|
||||
|
||||
template <class C>
|
||||
using specific_member_function = typename helper::template specific_member_function<C>;
|
||||
|
||||
template <class C>
|
||||
using const_specific_member_function = typename helper::template const_specific_member_function<C>;
|
||||
|
||||
using generic_class = typename helper::generic_class;
|
||||
using generic_member_function = typename helper::generic_member_function;
|
||||
|
||||
template <class C>
|
||||
using member_abi_function = typename helper::template member_abi_function<C>;
|
||||
|
||||
using generic_function_storage = typename helper::generic_function_storage;
|
||||
using traits = mfp_traits<R, Targs...>;
|
||||
|
||||
pmfp_base()
|
||||
: m_obj(nullptr)
|
||||
: helper()
|
||||
{
|
||||
// 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_base(specific_member_function<O> mftp, O *object)
|
||||
: m_obj(nullptr)
|
||||
template<typename O, typename P>
|
||||
pmfp_base(typename traits::template specific_member_function<O> mftp, P *object)
|
||||
: helper()
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved);
|
||||
std::fill(s, s + sizeof(m_resolved), 0);
|
||||
bind<specific_member_function<O>>(object, &mftp);
|
||||
this->bind(static_cast<O*>(object), &mftp);
|
||||
}
|
||||
|
||||
template<typename O>
|
||||
pmfp_base(const_specific_member_function<O> mftp, O *object)
|
||||
: m_obj(nullptr)
|
||||
template<typename O, typename P>
|
||||
pmfp_base(typename traits::template const_specific_member_function<O> mftp, P *object)
|
||||
: helper()
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved);
|
||||
std::fill(s, s + sizeof(m_resolved), 0);
|
||||
bind<const_specific_member_function<O>>(object, &mftp);
|
||||
this->bind(static_cast<O*>(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<typename O>
|
||||
void set(specific_member_function<O> mftp, O *object)
|
||||
void set(typename traits::template specific_member_function<O> mftp, O *object)
|
||||
{
|
||||
bind<specific_member_function<O>>(object, &mftp);
|
||||
this->bind(object, &mftp);
|
||||
}
|
||||
|
||||
R operator()(Targs... args) const noexcept(true)
|
||||
@ -382,48 +443,17 @@ namespace plib {
|
||||
return this->call(std::forward<Targs>(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<typename SPC, typename O, typename MF>
|
||||
void bind(O * object, MF *fraw)
|
||||
{
|
||||
SPC pFunc;
|
||||
static_assert(sizeof(pFunc) >= sizeof(MF), "size error");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
*reinterpret_cast<MF *>(&pFunc) = *fraw;
|
||||
|
||||
auto r = helper::template get<member_abi_function<O>>(pFunc, object);
|
||||
|
||||
static_assert(sizeof(m_resolved) >= sizeof(r.first), "size mismatch 1");
|
||||
static_assert(sizeof(m_resolved) >= sizeof(member_abi_function<O>), "size mismatch 2");
|
||||
|
||||
//*reinterpret_cast<member_abi_function<O> *>(&m_resolved) = r.first;
|
||||
reinterpret_copy(r.first, m_resolved);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
m_obj = reinterpret_cast<generic_class *>(r.second);
|
||||
}
|
||||
|
||||
template<typename O>
|
||||
R call(O *obj, Targs&&... args) const noexcept(true)
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
return helper::call(reinterpret_cast<member_abi_function<O> *>(&m_resolved),
|
||||
obj, std::forward<Targs>(args)...);
|
||||
}
|
||||
|
||||
R call(Targs&&... args) const noexcept(true)
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
return helper::call(reinterpret_cast<const member_abi_function<generic_class> *>(&m_resolved),
|
||||
m_obj, std::forward<Targs>(args)...);
|
||||
}
|
||||
|
||||
generic_function_storage m_resolved;
|
||||
generic_class *m_obj;
|
||||
};
|
||||
|
||||
template<typename R, typename... Targs>
|
||||
using pmfp = pmfp_base<ppmf_type::value, ppmf_internal::value, R, Targs...>;
|
||||
template<typename Signature>
|
||||
using pmfp = pmfp_base<ppmf_internal::value, Signature>;
|
||||
|
||||
///
|
||||
/// \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<typename T>
|
||||
class late_pmfp
|
||||
@ -446,41 +476,33 @@ namespace plib {
|
||||
public:
|
||||
|
||||
using return_type = T;
|
||||
|
||||
template <class C>
|
||||
using specific_member_function = typename return_type::template specific_member_function<C>; // 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<mfp_generic_class>;
|
||||
using static_creator = return_type (*)(const generic_member_function *, mfp_generic_class *);
|
||||
|
||||
template<typename O>
|
||||
late_pmfp(specific_member_function<O> mftp)
|
||||
late_pmfp(typename traits::template specific_member_function<O> mftp)
|
||||
: m_creator(creator<O>)
|
||||
{
|
||||
static_assert(sizeof(m_raw) >= sizeof(specific_member_function<O>), "size issue");
|
||||
static_assert(sizeof(m_raw) >= sizeof(typename traits::template specific_member_function<O>), "size issue");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
*reinterpret_cast<specific_member_function<O> *>(&m_raw) = mftp;
|
||||
*reinterpret_cast<typename traits::template specific_member_function<O> *>(&m_raw) = mftp;
|
||||
}
|
||||
|
||||
template<typename O>
|
||||
return_type operator()(O *object) const
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
return m_creator(&m_raw, reinterpret_cast<generic_class *>(object));
|
||||
return m_creator(&m_raw, reinterpret_cast<mfp_generic_class *>(object));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template <typename O>
|
||||
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<const specific_member_function<O> *>(raw);
|
||||
auto p = reinterpret_cast<const typename traits::template specific_member_function<O> *>(raw);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
auto o = reinterpret_cast<O *>(obj);
|
||||
return return_type(*p, o);
|
||||
|
@ -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<bool, true>;
|
||||
#ifdef UNICODE
|
||||
using unicode = std::integral_constant<bool, true>;
|
||||
#else
|
||||
using unicode = std::integral_constant<bool, false>;
|
||||
#endif
|
||||
#ifdef UNICODE
|
||||
using unicode = std::integral_constant<bool, true>;
|
||||
#else
|
||||
using unicode = std::integral_constant<bool, false>;
|
||||
#endif
|
||||
#else
|
||||
using win32 = std::integral_constant<bool, false>;
|
||||
using unicode = std::integral_constant<bool, true>;
|
||||
@ -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<ci_compiler, ci_compiler::NVCC>;
|
||||
using version = std::integral_constant<int, NVCCBUILD>;
|
||||
#elif defined(_MSC_VER)
|
||||
using type = std::integral_constant<ci_compiler, ci_compiler::MSC>;
|
||||
using version = std::integral_constant<int, _MSC_VER>;
|
||||
#elif defined(__clang__)
|
||||
using type = std::integral_constant<ci_compiler, ci_compiler::CLANG>;
|
||||
using version = std::integral_constant<int, (__clang_major__) * 100 + (__clang_minor__)>;
|
||||
#elif defined(__GNUC__)
|
||||
using type = std::integral_constant<ci_compiler, ci_compiler::GCC>;
|
||||
using version = std::integral_constant<int, (__GNUC__) * 100 + (__GNUC_MINOR__)>;
|
||||
#elif defined(_MSC_VER)
|
||||
using type = std::integral_constant<ci_compiler, ci_compiler::MSC>;
|
||||
using version = std::integral_constant<int, _MSC_VER>;
|
||||
#else
|
||||
using type = std::integral_constant<ci_compiler, ci_compiler::UNKNOWN>;
|
||||
using version = std::integral_constant<int, 0>;
|
||||
@ -174,6 +178,8 @@ namespace plib
|
||||
using arch = std::integral_constant<ci_arch, ci_arch::ARM>;
|
||||
#elif defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64)
|
||||
using arch = std::integral_constant<ci_arch, ci_arch::MIPS>;
|
||||
#elif defined(__ia64__)
|
||||
using arch = std::integral_constant<ci_arch, ci_arch::IA64>;
|
||||
#else
|
||||
using arch = std::integral_constant<ci_arch, ci_arch::UNKNOWN>;
|
||||
#endif
|
||||
@ -182,6 +188,11 @@ namespace plib
|
||||
#else
|
||||
using mingw = std::integral_constant<bool, false>;
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
using clang_apple_noexcept_issue = std::integral_constant<bool, version::value < 1100>;
|
||||
#else
|
||||
using clang_apple_noexcept_issue = std::integral_constant<bool, false>;
|
||||
#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<std::ostream, int>:: value should be true
|
||||
///
|
||||
/// \tparam LEFT Stream type
|
||||
/// \tparam RIGHT Type to check for operator overload
|
||||
|
||||
template<class LEFT, class RIGHT>
|
||||
struct has_ostream_operator_impl {
|
||||
template<class V> static auto test(V*) -> decltype(std::declval<LEFT &>() << std::declval<V>());
|
||||
template<typename> static auto test(...) -> std::false_type;
|
||||
//static constexpr const bool value = std::is_same<LEFT &, decltype(test<RIGHT>(0))>::value;
|
||||
using type = typename std::is_same<LEFT &, decltype(test<RIGHT>(nullptr))>::type;
|
||||
};
|
||||
template<class LEFT, class RIGHT>
|
||||
struct has_ostream_operator : has_ostream_operator_impl<LEFT, RIGHT>::type {};
|
||||
|
||||
} // namespace plib
|
||||
|
||||
|
@ -177,7 +177,7 @@ private:
|
||||
class log_processor
|
||||
{
|
||||
public:
|
||||
using callback_type = plib::pmfp<void, std::size_t, double, double>;
|
||||
using callback_type = plib::pmfp<void (std::size_t, double, double)>;
|
||||
|
||||
struct elem
|
||||
{
|
||||
@ -256,7 +256,7 @@ private:
|
||||
|
||||
struct aggregator
|
||||
{
|
||||
using callback_type = plib::pmfp<void, std::size_t, double, double>;
|
||||
using callback_type = plib::pmfp<void (std::size_t, double, double)>;
|
||||
|
||||
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<void, std::size_t, double, double>;
|
||||
using callback_type = plib::pmfp<void (std::size_t, double, double)>;
|
||||
|
||||
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<void, std::size_t, double, double>;
|
||||
using callback_type = plib::pmfp<void (std::size_t, double, double)>;
|
||||
|
||||
filter_lp(double freq, std::size_t channels, callback_type cb)
|
||||
: m_cb(cb)
|
||||
|
71
src/lib/netlist/tests/test_pmfp.cpp
Executable file
71
src/lib/netlist/tests/test_pmfp.cpp
Executable file
@ -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 <utility>
|
||||
#include <memory>
|
||||
|
||||
/// plib::late_pmfp<plib::pmfp<void, pstring>> 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<plib::pmfp<void (int &)>> 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<PPMF_TYPE_PMF, void(int)> mfp_PPMF_TYPE_PMF;
|
||||
plib::pmfp_base<PPMF_TYPE_INTERNAL_ITANIUM, void(int)> mfp_PPMF_TYPE_INTERNAL_ITANIUM;
|
||||
plib::pmfp_base<PPMF_TYPE_INTERNAL_ARM, void(int)> mfp_PPMF_TYPE_INTERNAL_ARM;
|
||||
plib::pmfp_base<PPMF_TYPE_INTERNAL_MSC, void(int)> 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<PPMF_TYPE_GNUC_PMF_CONV, void(int)> 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
|
||||
}
|
360
src/lib/netlist/tests/test_pmfp_multibase.cpp
Normal file
360
src/lib/netlist/tests/test_pmfp_multibase.cpp
Normal file
@ -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 <utility>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <iostream>
|
||||
|
||||
template <class F, class S>
|
||||
typename std::enable_if_t<!plib::has_ostream_operator<std::basic_ostream<char>, std::pair<F, S>>::value, std::basic_ostream<char>&>
|
||||
operator << (std::basic_ostream<char>& os, const std::pair<F, S> &p)
|
||||
{
|
||||
os << "{ " << p.first << ", " << p.second << " }";
|
||||
return os;
|
||||
}
|
||||
|
||||
#include "plib/ptests.h"
|
||||
|
||||
template <class T>
|
||||
typename std::enable_if_t<!plib::has_ostream_operator<std::basic_ostream<char>, T>::value, std::basic_ostream<char>&>
|
||||
operator << (std::basic_ostream<char>& 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<void *, int>;
|
||||
|
||||
//using test_delegate1 = plib::pmfp<ret_t (int, int)>;
|
||||
using test_delegate = plib::pmfp<ret_t ()>;
|
||||
|
||||
class a
|
||||
{
|
||||
public:
|
||||
ret_t ap() { return ret_t(static_cast<void *>(this), ax); }
|
||||
int ax;
|
||||
};
|
||||
|
||||
class b
|
||||
{
|
||||
public:
|
||||
ret_t bp() { return ret_t(static_cast<void *>(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<void *>(this), 1); }
|
||||
virtual ret_t vp2() { return ret_t(static_cast<void *>(this), 2); }
|
||||
};
|
||||
|
||||
class vti1 : public vtb
|
||||
{
|
||||
public:
|
||||
virtual ret_t vp1() override { return ret_t(static_cast<void *>(this), 3); }
|
||||
virtual ret_t vp2() override { return ret_t(static_cast<void *>(this), 4); }
|
||||
};
|
||||
|
||||
class vti2 : public a, public vtb
|
||||
{
|
||||
public:
|
||||
virtual ret_t vp1() override { return ret_t(static_cast<void *>(this), 13); }
|
||||
virtual ret_t vp2() override { return ret_t(static_cast<void *>(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<void *>(this), x); }
|
||||
int x=10;
|
||||
};
|
||||
|
||||
class vd1 : public virtual vb
|
||||
{
|
||||
public:
|
||||
ret_t yp() { return ret_t(static_cast<void *>(this), y); }
|
||||
int y;
|
||||
};
|
||||
|
||||
class vd2 : public virtual vb
|
||||
{
|
||||
public:
|
||||
ret_t zp() { return ret_t(static_cast<void *>(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<void *, int>;
|
||||
|
||||
//using test_delegate1 = plib::pmfp<ret_t (int, int)>;
|
||||
using test_delegate = plib::pmfp<void (ret_t &)>;
|
||||
|
||||
class a
|
||||
{
|
||||
public:
|
||||
void ap(ret_t &r) { r = ret_t(static_cast<void *>(this), ax); }
|
||||
int ax;
|
||||
};
|
||||
|
||||
class b
|
||||
{
|
||||
public:
|
||||
void bp(ret_t &r) { r = ret_t(static_cast<void *>(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<void *>(this), 1); }
|
||||
virtual void vp2(ret_t &r) { r = ret_t(static_cast<void *>(this), 2); }
|
||||
};
|
||||
|
||||
class vti1 : public vtb
|
||||
{
|
||||
public:
|
||||
virtual void vp1(ret_t &r) override { r = ret_t(static_cast<void *>(this), 3); }
|
||||
virtual void vp2(ret_t &r) override { r = ret_t(static_cast<void *>(this), 4); }
|
||||
};
|
||||
|
||||
class vti2 : public a, public vtb
|
||||
{
|
||||
public:
|
||||
virtual void vp1(ret_t &r) override { r = ret_t(static_cast<void *>(this), 13); }
|
||||
virtual void vp2(ret_t &r) override { r = ret_t(static_cast<void *>(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<void *>(this), x); }
|
||||
int x=10;
|
||||
};
|
||||
|
||||
class vd1 : public virtual vb
|
||||
{
|
||||
public:
|
||||
void yp(ret_t &r) { r = ret_t(static_cast<void *>(this), y); }
|
||||
int y;
|
||||
};
|
||||
|
||||
class vd2 : public virtual vb
|
||||
{
|
||||
public:
|
||||
void zp(ret_t &r) { r = ret_t(static_cast<void *>(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<void *>(static_cast<a *>(&obj)), 1));
|
||||
auto g_ret = g();
|
||||
PEXPECT_EQ(g_ret, ret_t(static_cast<void *>(static_cast<b *>(&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<vtb> 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<void *>(static_cast<vb *>(&obj2)), static_cast<void *>(static_cast<vd1 *>(&obj2)));
|
||||
PEXPECT_NE(static_cast<void *>(static_cast<vb *>(&obj2)), static_cast<void *>(static_cast<vd2 *>(&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<void *>(static_cast<a *>(&obj)), 1));
|
||||
ret_t gr;
|
||||
g(gr);
|
||||
PEXPECT_EQ(gr, ret_t(static_cast<void *>(static_cast<b *>(&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<vtb> 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<void *>(static_cast<vb *>(&obj2)), static_cast<void *>(static_cast<vd1 *>(&obj2)));
|
||||
PEXPECT_NE(static_cast<void *>(static_cast<vb *>(&obj2)), static_cast<void *>(static_cast<vd2 *>(&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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user