util/strformat.h, util/delegate.h: More cleanup and future-proofing.
* util/strformat.h: Found a SFINAE trick to detect absence of stream-out operators. Fixes building with C++20 standard library (#6275). * util/delegate.h: Fixed a commend and removed an unused declaration from MSVC member function pointer wrapper. * util/delegate.h: Added support for discarding functoid return values for delegates returning void. * util/delegate.h: Added support for using std::ref to bind non-copyable functoids.
This commit is contained in:
parent
6e3adb7199
commit
d1e68f25dd
@ -24,6 +24,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
namespace util::detail {
|
||||
|
||||
//**************************************************************************
|
||||
// GLOBAL VARIABLES
|
||||
@ -31,7 +32,7 @@
|
||||
|
||||
#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE)
|
||||
|
||||
delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { { 0 } };
|
||||
const delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { { 0 } };
|
||||
|
||||
#endif
|
||||
|
||||
@ -82,3 +83,5 @@ delegate_generic_function delegate_mfp::convert_to_generic(delegate_generic_clas
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace util::detail
|
||||
|
@ -82,6 +82,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
@ -97,7 +98,9 @@
|
||||
#define DELEGATE_TYPE_MSVC 2
|
||||
|
||||
// select which one we will be using
|
||||
#if defined(__GNUC__)
|
||||
#if defined(FORCE_COMPATIBLE)
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
|
||||
#elif defined(__GNUC__)
|
||||
/* 32bit MINGW asks for different convention */
|
||||
#if defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__)
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_INTERNAL
|
||||
@ -110,18 +113,11 @@
|
||||
#define MEMBER_ABI
|
||||
#define HAS_DIFFERENT_ABI 0
|
||||
#endif
|
||||
#elif defined(_MSC_VER) && defined (_M_X64)
|
||||
#define MEMBER_ABI
|
||||
#define HAS_DIFFERENT_ABI 0
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_MSVC
|
||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||
#define MEMBER_ABI
|
||||
#define HAS_DIFFERENT_ABI 0
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_MSVC
|
||||
#else
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
|
||||
#endif
|
||||
|
||||
#if defined(FORCE_COMPATIBLE)
|
||||
#undef USE_DELEGATE_TYPE
|
||||
#undef MEMBER_ABI
|
||||
#undef HAS_DIFFERENT_ABI
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
|
||||
#endif
|
||||
|
||||
@ -129,6 +125,10 @@
|
||||
#define MEMBER_ABI
|
||||
#define HAS_DIFFERENT_ABI 0
|
||||
#endif
|
||||
|
||||
|
||||
namespace util::detail {
|
||||
|
||||
//**************************************************************************
|
||||
// HELPER CLASSES
|
||||
//**************************************************************************
|
||||
@ -148,31 +148,6 @@ class delegate_generic_class;
|
||||
#endif
|
||||
|
||||
|
||||
// ======================> delegate_late_bind
|
||||
|
||||
// simple polymorphic class that must be mixed into any object that is late-bound
|
||||
class delegate_late_bind
|
||||
{
|
||||
public:
|
||||
delegate_late_bind() { }
|
||||
virtual ~delegate_late_bind() { }
|
||||
};
|
||||
|
||||
|
||||
// ======================> binding_type_exception
|
||||
|
||||
// exception that is thrown when a bind fails the dynamic_cast
|
||||
class binding_type_exception : public std::exception
|
||||
{
|
||||
public:
|
||||
binding_type_exception(const std::type_info &target_type, const std::type_info &actual_type)
|
||||
: m_target_type(target_type), m_actual_type(actual_type)
|
||||
{ }
|
||||
const std::type_info &m_target_type;
|
||||
const std::type_info &m_actual_type;
|
||||
};
|
||||
|
||||
|
||||
// ======================> delegate_traits
|
||||
|
||||
// delegate_traits is a meta-template that is used to provide a static function pointer
|
||||
@ -212,11 +187,7 @@ public:
|
||||
{ }
|
||||
|
||||
// copy constructor
|
||||
delegate_mfp(const delegate_mfp &src)
|
||||
: m_rawdata(src.m_rawdata)
|
||||
, m_realobject(src.m_realobject)
|
||||
, m_stubfunction(src.m_stubfunction)
|
||||
{ }
|
||||
delegate_mfp(const delegate_mfp &src) = default;
|
||||
|
||||
// construct from any member function pointer
|
||||
template <typename MemberFunctionType, class MemberFunctionClass, typename ReturnType, typename StaticFunctionType>
|
||||
@ -225,7 +196,7 @@ public:
|
||||
, m_realobject(nullptr)
|
||||
, m_stubfunction(make_generic<StaticFunctionType>(&delegate_mfp::method_stub<MemberFunctionClass, ReturnType>))
|
||||
{
|
||||
assert(sizeof(mfp) <= sizeof(m_rawdata));
|
||||
static_assert(sizeof(mfp) <= sizeof(m_rawdata), "Unsupported member function pointer size");
|
||||
*reinterpret_cast<MemberFunctionType *>(&m_rawdata) = mfp;
|
||||
}
|
||||
|
||||
@ -280,7 +251,8 @@ private:
|
||||
raw_mfp_data m_rawdata; // raw buffer to hold the copy of the function pointer
|
||||
delegate_generic_class * m_realobject; // pointer to the object used for calling
|
||||
delegate_generic_function m_stubfunction; // pointer to our matching stub function
|
||||
static raw_mfp_data s_null_mfp; // nullptr mfp
|
||||
|
||||
static const raw_mfp_data s_null_mfp; // nullptr mfp
|
||||
};
|
||||
|
||||
#elif (USE_DELEGATE_TYPE == DELEGATE_TYPE_INTERNAL)
|
||||
@ -292,28 +264,22 @@ class delegate_mfp
|
||||
{
|
||||
public:
|
||||
// default constructor
|
||||
delegate_mfp()
|
||||
: m_function(0)
|
||||
, m_this_delta(0)
|
||||
{ }
|
||||
delegate_mfp() = default;
|
||||
|
||||
// copy constructor
|
||||
delegate_mfp(const delegate_mfp &src)
|
||||
: m_function(src.m_function)
|
||||
, m_this_delta(src.m_this_delta)
|
||||
{ }
|
||||
delegate_mfp(const delegate_mfp &src) = default;
|
||||
|
||||
// construct from any member function pointer
|
||||
template <typename MemberFunctionType, class MemberFunctionClass, typename ReturnType, typename StaticFunctionType>
|
||||
delegate_mfp(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType)
|
||||
{
|
||||
assert(sizeof(mfp) == sizeof(*this));
|
||||
static_assert(sizeof(mfp) == sizeof(*this), "Unsupported member function pointer size");
|
||||
*reinterpret_cast<MemberFunctionType *>(this) = mfp;
|
||||
}
|
||||
|
||||
// comparison helpers
|
||||
bool operator==(const delegate_mfp &rhs) const { return (m_function == rhs.m_function && m_this_delta == rhs.m_this_delta); }
|
||||
bool isnull() const { return (m_function == 0 && m_this_delta == 0); }
|
||||
bool operator==(const delegate_mfp &rhs) const { return (m_function == rhs.m_function) && (m_this_delta == rhs.m_this_delta); }
|
||||
bool isnull() const { return !m_function && !m_this_delta; }
|
||||
|
||||
// getters
|
||||
static delegate_generic_class *real_object(delegate_generic_class *original) { return original; }
|
||||
@ -330,45 +296,41 @@ private:
|
||||
delegate_generic_function convert_to_generic(delegate_generic_class *&object) const;
|
||||
|
||||
// actual state
|
||||
uintptr_t m_function; // first item can be one of two things:
|
||||
uintptr_t m_function = 0; // 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_this_delta = 0; // delta to apply to the 'this' pointer
|
||||
};
|
||||
|
||||
#elif (USE_DELEGATE_TYPE == DELEGATE_TYPE_MSVC)
|
||||
|
||||
// ======================> delegate_mfp
|
||||
const int SINGLE_MEMFUNCPTR_SIZE = sizeof(void (delegate_generic_class::*)());
|
||||
|
||||
// struct describing the contents of a member function pointer
|
||||
class delegate_mfp
|
||||
{
|
||||
struct single_base_equiv { delegate_generic_function p; };
|
||||
struct multi_base_equiv { delegate_generic_function p; int o; };
|
||||
|
||||
public:
|
||||
// default constructor
|
||||
delegate_mfp()
|
||||
: m_function(0), m_this_delta(0), m_dummy1(0), m_dummy2(0), m_size(0)
|
||||
{
|
||||
}
|
||||
delegate_mfp() = default;
|
||||
|
||||
// copy constructor
|
||||
delegate_mfp(const delegate_mfp &src)
|
||||
: m_function(src.m_function), m_this_delta(src.m_this_delta), m_dummy1(src.m_dummy1), m_dummy2(src.m_dummy2), m_size(src.m_size)
|
||||
{
|
||||
}
|
||||
delegate_mfp(const delegate_mfp &src) = default;
|
||||
|
||||
// construct from any member function pointer
|
||||
template <typename MemberFunctionType, class MemberFunctionClass, typename ReturnType, typename StaticFunctionType>
|
||||
delegate_mfp(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType)
|
||||
{
|
||||
static_assert(sizeof(mfp) == sizeof(void *) || sizeof(mfp) == 2 * sizeof(void *));
|
||||
static_assert((sizeof(mfp) == sizeof(single_base_equiv)) || (sizeof(mfp) == sizeof(multi_base_equiv)), "Unsupported member function pointer size");
|
||||
m_size = sizeof(mfp);
|
||||
*reinterpret_cast<MemberFunctionType *>(this) = mfp;
|
||||
}
|
||||
|
||||
// comparison helpers
|
||||
bool operator==(const delegate_mfp &rhs) const { return (m_function == rhs.m_function); }
|
||||
bool isnull() const { return (m_function == 0); }
|
||||
bool operator==(const delegate_mfp &rhs) const { return m_function == rhs.m_function; }
|
||||
bool isnull() const { return !m_function; }
|
||||
|
||||
// getters
|
||||
static delegate_generic_class *real_object(delegate_generic_class *original) { return original; }
|
||||
@ -378,28 +340,50 @@ public:
|
||||
void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
|
||||
{
|
||||
funcptr = reinterpret_cast<FunctionType>(m_function);
|
||||
if (m_size > SINGLE_MEMFUNCPTR_SIZE)
|
||||
if (m_size > sizeof(single_base_equiv))
|
||||
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
|
||||
}
|
||||
|
||||
private:
|
||||
// extract the generic function and adjust the object pointer
|
||||
delegate_generic_function convert_to_generic(delegate_generic_class *&object) const;
|
||||
|
||||
// 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
|
||||
uintptr_t m_function = 0; // pointer to function or non-virtual thunk for vtable index
|
||||
int m_this_delta = 0; // delta to apply to the 'this' pointer for multiple inheritance
|
||||
|
||||
int m_dummy1;
|
||||
int m_dummy2;
|
||||
int m_dummy1 = 0;
|
||||
int m_dummy2 = 0;
|
||||
|
||||
int m_size;
|
||||
unsigned m_size = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace util::detail
|
||||
|
||||
|
||||
|
||||
// ======================> delegate_late_bind
|
||||
|
||||
// simple polymorphic class that must be mixed into any object that is late-bound
|
||||
class delegate_late_bind
|
||||
{
|
||||
public:
|
||||
delegate_late_bind() { }
|
||||
virtual ~delegate_late_bind() { }
|
||||
};
|
||||
|
||||
|
||||
// ======================> binding_type_exception
|
||||
|
||||
// exception that is thrown when a bind fails the dynamic_cast
|
||||
class binding_type_exception : public std::exception
|
||||
{
|
||||
public:
|
||||
binding_type_exception(const std::type_info &target_type, const std::type_info &actual_type)
|
||||
: m_target_type(target_type), m_actual_type(actual_type)
|
||||
{ }
|
||||
const std::type_info &m_target_type;
|
||||
const std::type_info &m_actual_type;
|
||||
};
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
@ -414,22 +398,16 @@ class delegate_base
|
||||
{
|
||||
public:
|
||||
// define our traits
|
||||
template <class FunctionClass> using traits = delegate_traits<FunctionClass, ReturnType, Params...>;
|
||||
using generic_static_func = typename traits<delegate_generic_class>::static_func_type;
|
||||
template <class FunctionClass> using traits = util::detail::delegate_traits<FunctionClass, ReturnType, Params...>;
|
||||
using generic_static_func = typename traits<util::detail::delegate_generic_class>::static_func_type;
|
||||
typedef MEMBER_ABI generic_static_func generic_member_func;
|
||||
|
||||
// generic constructor
|
||||
delegate_base()
|
||||
: m_function(nullptr)
|
||||
, m_object(nullptr)
|
||||
, m_latebinder(nullptr)
|
||||
, m_raw_function(nullptr)
|
||||
{ }
|
||||
delegate_base() = default;
|
||||
|
||||
// copy constructor
|
||||
delegate_base(const delegate_base &src)
|
||||
: m_function(src.m_function)
|
||||
, m_object(nullptr)
|
||||
, m_latebinder(src.m_latebinder)
|
||||
, m_raw_function(src.m_raw_function)
|
||||
, m_raw_mfp(src.m_raw_mfp)
|
||||
@ -440,7 +418,6 @@ public:
|
||||
// copy constructor with late bind
|
||||
delegate_base(const delegate_base &src, delegate_late_bind &object)
|
||||
: m_function(src.m_function)
|
||||
, m_object(nullptr)
|
||||
, m_latebinder(src.m_latebinder)
|
||||
, m_raw_function(src.m_raw_function)
|
||||
, m_raw_mfp(src.m_raw_mfp)
|
||||
@ -451,35 +428,28 @@ public:
|
||||
// construct from member function with object pointer
|
||||
template <class FunctionClass>
|
||||
delegate_base(typename traits<FunctionClass>::member_func_type funcptr, FunctionClass *object)
|
||||
: m_function(nullptr)
|
||||
, m_object(nullptr)
|
||||
, m_latebinder(&late_bind_helper<FunctionClass>)
|
||||
, m_raw_function(nullptr)
|
||||
: m_latebinder(&late_bind_helper<FunctionClass>)
|
||||
, m_raw_mfp(funcptr, object, static_cast<ReturnType *>(nullptr), static_cast<generic_static_func>(nullptr))
|
||||
{
|
||||
bind(reinterpret_cast<delegate_generic_class *>(object));
|
||||
bind(reinterpret_cast<util::detail::delegate_generic_class *>(object));
|
||||
}
|
||||
|
||||
template <class FunctionClass>
|
||||
delegate_base(typename traits<FunctionClass>::const_member_func_type funcptr, FunctionClass *object)
|
||||
: m_function(nullptr)
|
||||
, m_object(nullptr)
|
||||
, m_latebinder(&late_bind_helper<FunctionClass>)
|
||||
, m_raw_function(nullptr)
|
||||
: m_latebinder(&late_bind_helper<FunctionClass>)
|
||||
, m_raw_mfp(funcptr, object, static_cast<ReturnType *>(nullptr), static_cast<generic_static_func>(nullptr))
|
||||
{
|
||||
bind(reinterpret_cast<delegate_generic_class *>(object));
|
||||
bind(reinterpret_cast<util::detail::delegate_generic_class *>(object));
|
||||
}
|
||||
|
||||
// construct from static reference function with object reference
|
||||
template <class FunctionClass>
|
||||
delegate_base(typename traits<FunctionClass>::static_ref_func_type funcptr, FunctionClass *object)
|
||||
: m_function(reinterpret_cast<generic_static_func>(funcptr))
|
||||
, m_object(nullptr)
|
||||
, m_latebinder(&late_bind_helper<FunctionClass>)
|
||||
, m_raw_function(reinterpret_cast<generic_static_func>(funcptr))
|
||||
{
|
||||
bind(reinterpret_cast<delegate_generic_class *>(object));
|
||||
bind(reinterpret_cast<util::detail::delegate_generic_class *>(object));
|
||||
}
|
||||
|
||||
// copy operator
|
||||
@ -501,7 +471,7 @@ public:
|
||||
// comparison helper
|
||||
bool operator==(const delegate_base &rhs) const
|
||||
{
|
||||
return (m_raw_function == rhs.m_raw_function && object() == rhs.object() && m_raw_mfp == rhs.m_raw_mfp);
|
||||
return (m_raw_function == rhs.m_raw_function) && (object() == rhs.object()) && (m_raw_mfp == rhs.m_raw_mfp);
|
||||
}
|
||||
|
||||
|
||||
@ -509,55 +479,59 @@ public:
|
||||
ReturnType operator()(Params... args) const
|
||||
{
|
||||
if (is_mfp() && (HAS_DIFFERENT_ABI))
|
||||
return (*reinterpret_cast<generic_member_func>(m_function)) (m_object, std::forward<Params>(args)...);
|
||||
return (*reinterpret_cast<generic_member_func>(m_function))(m_object, std::forward<Params>(args)...);
|
||||
else
|
||||
return (*m_function) (m_object, std::forward<Params>(args)...);
|
||||
return (*m_function)(m_object, std::forward<Params>(args)...);
|
||||
}
|
||||
|
||||
// getters
|
||||
bool has_object() const { return (object() != nullptr); }
|
||||
bool has_object() const { return object() != nullptr; }
|
||||
|
||||
// helpers
|
||||
bool isnull() const { return (m_raw_function == nullptr && m_raw_mfp.isnull()); }
|
||||
bool isnull() const { return !m_raw_function && m_raw_mfp.isnull(); }
|
||||
bool is_mfp() const { return !m_raw_mfp.isnull(); }
|
||||
|
||||
// late binding
|
||||
void late_bind(delegate_late_bind &object) { if(m_latebinder) bind((*m_latebinder)(object)); }
|
||||
void late_bind(delegate_late_bind &object)
|
||||
{
|
||||
if (m_latebinder)
|
||||
bind((*m_latebinder)(object));
|
||||
}
|
||||
|
||||
protected:
|
||||
// return the actual object (not the one we use for calling)
|
||||
delegate_generic_class *object() const { return is_mfp() ? m_raw_mfp.real_object(m_object) : m_object; }
|
||||
util::detail::delegate_generic_class *object() const { return is_mfp() ? m_raw_mfp.real_object(m_object) : m_object; }
|
||||
|
||||
// late binding function
|
||||
using late_bind_func = delegate_generic_class*(*)(delegate_late_bind &object);
|
||||
using late_bind_func = util::detail::delegate_generic_class*(*)(delegate_late_bind &object);
|
||||
|
||||
// late binding helper
|
||||
template <class FunctionClass>
|
||||
static delegate_generic_class *late_bind_helper(delegate_late_bind &object)
|
||||
static util::detail::delegate_generic_class *late_bind_helper(delegate_late_bind &object)
|
||||
{
|
||||
FunctionClass *result = dynamic_cast<FunctionClass *>(&object);
|
||||
if (!result)
|
||||
throw binding_type_exception(typeid(FunctionClass), typeid(object));
|
||||
|
||||
return reinterpret_cast<delegate_generic_class *>(result);
|
||||
return reinterpret_cast<util::detail::delegate_generic_class *>(result);
|
||||
}
|
||||
|
||||
// bind the actual object
|
||||
void bind(delegate_generic_class *object)
|
||||
void bind(util::detail::delegate_generic_class *object)
|
||||
{
|
||||
m_object = object;
|
||||
|
||||
// if we're wrapping a member function pointer, handle special stuff
|
||||
if (m_object != nullptr && is_mfp())
|
||||
if (m_object && is_mfp())
|
||||
m_raw_mfp.update_after_bind(m_function, m_object);
|
||||
}
|
||||
|
||||
// internal state
|
||||
generic_static_func m_function; // resolved static function pointer
|
||||
delegate_generic_class * m_object; // resolved object to the post-cast object
|
||||
late_bind_func m_latebinder; // late binding helper
|
||||
generic_static_func m_raw_function; // raw static function pointer
|
||||
delegate_mfp m_raw_mfp; // raw member function pointer
|
||||
generic_static_func m_function = nullptr; // resolved static function pointer
|
||||
util::detail::delegate_generic_class * m_object = nullptr; // resolved object to the post-cast object
|
||||
late_bind_func m_latebinder = nullptr; // late binding helper
|
||||
generic_static_func m_raw_function = nullptr; // raw static function pointer
|
||||
util::detail::delegate_mfp m_raw_mfp; // raw member function pointer
|
||||
};
|
||||
|
||||
|
||||
@ -576,35 +550,47 @@ private:
|
||||
using basetype = delegate_base<ReturnType, Params...>;
|
||||
using functoid_setter = void (*)(delegate &);
|
||||
|
||||
template <typename T> struct functoid_type_unwrap { using type = std::remove_reference_t<T>; };
|
||||
template <typename T> struct functoid_type_unwrap<std::reference_wrapper<T> > { using type = typename functoid_type_unwrap<T>::type; };
|
||||
template <typename T> using unwrapped_functoid_t = typename functoid_type_unwrap<std::remove_cv_t<std::remove_reference_t<T> > >::type;
|
||||
|
||||
template <typename T> static constexpr bool matching_non_const_call(T &&) { return false; }
|
||||
template <typename T> static constexpr bool matching_non_const_call(ReturnType (T::*)(Params...)) { return true; }
|
||||
template <typename T> static constexpr bool matching_const_call(T &&) { return false; }
|
||||
template <typename T> static constexpr bool matching_const_call(ReturnType (T::*)(Params...) const) { return true; }
|
||||
|
||||
template <typename T> static T *unwrap_functoid(T *functoid) { return functoid; }
|
||||
template <typename T> static T *unwrap_functoid(std::reference_wrapper<T> *functoid) { return &functoid->get(); }
|
||||
|
||||
template <typename T>
|
||||
unwrapped_functoid_t<T> *unwrap_functoid() noexcept
|
||||
{
|
||||
return unwrap_functoid(std::any_cast<std::remove_cv_t<std::remove_reference_t<T> > >(&m_functoid));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static functoid_setter make_functoid_setter()
|
||||
{
|
||||
using unwrapped_type = std::remove_cv_t<std::remove_reference_t<T> >;
|
||||
if constexpr (matching_non_const_call(&unwrapped_type::operator()))
|
||||
if constexpr (matching_non_const_call(&unwrapped_functoid_t<T>::operator()))
|
||||
{
|
||||
return
|
||||
[] (delegate &obj)
|
||||
{
|
||||
obj.basetype::operator=(
|
||||
basetype(
|
||||
static_cast<ReturnType (unwrapped_type::*)(Params...)>(&unwrapped_type::operator()),
|
||||
std::any_cast<unwrapped_type>(&obj.m_functoid)));
|
||||
static_cast<ReturnType (unwrapped_functoid_t<T>::*)(Params...)>(&unwrapped_functoid_t<T>::operator()),
|
||||
obj.unwrap_functoid<T>()));
|
||||
};
|
||||
}
|
||||
else if constexpr (matching_const_call(&unwrapped_type::operator()))
|
||||
else if constexpr (matching_const_call(&unwrapped_functoid_t<T>::operator()))
|
||||
{
|
||||
return
|
||||
[] (delegate &obj)
|
||||
{
|
||||
obj.basetype::operator=(
|
||||
basetype(
|
||||
static_cast<ReturnType (unwrapped_type::*)(Params...) const>(&unwrapped_type::operator()),
|
||||
std::any_cast<unwrapped_type>(&obj.m_functoid)));
|
||||
static_cast<ReturnType (unwrapped_functoid_t<T>::*)(Params...) const>(&unwrapped_functoid_t<T>::operator()),
|
||||
obj.unwrap_functoid<T>()));
|
||||
};
|
||||
}
|
||||
else
|
||||
@ -614,8 +600,8 @@ private:
|
||||
{
|
||||
obj.basetype::operator=(
|
||||
basetype(
|
||||
[] (unwrapped_type &f, Params... args) { return ReturnType(f(std::forward<Params>(args)...)); },
|
||||
std::any_cast<unwrapped_type>(&obj.m_functoid)));
|
||||
[] (unwrapped_functoid_t<T> &f, Params... args) { return ReturnType(f(std::forward<Params>(args)...)); },
|
||||
obj.unwrap_functoid<T>()));
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -629,7 +615,7 @@ protected:
|
||||
template <class FunctionClass> using const_member_func_type = typename traits<FunctionClass>::const_member_func_type;
|
||||
template <class FunctionClass> using static_ref_func_type = typename traits<FunctionClass>::static_ref_func_type;
|
||||
|
||||
template <typename T> using suitable_functoid = std::is_convertible<std::invoke_result_t<T, Params...>, ReturnType>;
|
||||
template <typename T> using suitable_functoid = std::bool_constant<std::is_convertible_v<std::invoke_result_t<T, Params...>, ReturnType> || std::is_same_v<std::void_t<std::invoke_result_t<T, Params...> >, ReturnType> >;
|
||||
|
||||
public:
|
||||
delegate() : basetype() { }
|
||||
|
@ -132,6 +132,7 @@
|
||||
- Inappropriate type for parameterised width/precision
|
||||
- Positional width/precision specifier not terminated with $
|
||||
- Inappropriate type for n conversion
|
||||
- Default conversion for type that lacks stream out operator
|
||||
|
||||
Some limitations have been described in passing. Major limitations
|
||||
and bugs include:
|
||||
@ -589,42 +590,30 @@ private:
|
||||
template <typename U>
|
||||
using unsigned_integer_semantics = std::bool_constant<std::is_integral_v<U> && !std::is_signed_v<U> >;
|
||||
|
||||
static void apply_signed(Stream &str, char16_t const &value)
|
||||
{
|
||||
str << std::make_signed_t<std::uint_least16_t>(std::uint_least16_t(value));
|
||||
}
|
||||
static void apply_signed(Stream &str, char32_t const &value)
|
||||
{
|
||||
str << std::make_signed_t<std::uint_least32_t>(std::uint_least32_t(value));
|
||||
}
|
||||
template <typename U>
|
||||
static std::enable_if_t<std::is_same_v<std::make_signed_t<U>, std::make_signed_t<char> > || std::is_integral_v<U> > apply_signed(Stream &str, U const &value)
|
||||
{
|
||||
if constexpr (std::is_same_v<std::make_signed_t<U>, std::make_signed_t<char> >)
|
||||
str << int(std::make_signed_t<U>(value));
|
||||
else if constexpr (std::is_signed_v<U>)
|
||||
else if constexpr (!std::is_signed_v<U> || std::is_same_v<typename Stream::char_type, U>)
|
||||
str << std::make_signed_t<U>(value);
|
||||
else if constexpr (std::is_invocable_v<decltype([] (auto &x, auto &y) -> decltype(x << y) { return x << y; }), Stream &, U const &>)
|
||||
str << value;
|
||||
else
|
||||
str << std::make_signed_t<U>(value);
|
||||
}
|
||||
|
||||
static void apply_unsigned(Stream &str, char16_t const &value)
|
||||
{
|
||||
str << std::uint_least16_t(value);
|
||||
}
|
||||
static void apply_unsigned(Stream &str, char32_t const &value)
|
||||
{
|
||||
str << std::uint_least32_t(value);
|
||||
}
|
||||
template <typename U>
|
||||
static std::enable_if_t<std::is_same_v<std::make_unsigned_t<U>, std::make_unsigned_t<char> > || std::is_integral_v<U> > apply_unsigned(Stream &str, U const &value)
|
||||
{
|
||||
if constexpr (std::is_same_v<std::make_unsigned_t<U>, std::make_unsigned_t<char> >)
|
||||
str << unsigned(std::make_unsigned_t<U>(value));
|
||||
else if constexpr (std::is_signed_v<U>)
|
||||
else if constexpr (!std::is_unsigned_v<U> || std::is_same_v<typename Stream::char_type, U>)
|
||||
str << std::make_unsigned_t<U>(value);
|
||||
else
|
||||
else if constexpr (std::is_invocable_v<decltype([] (auto &x, auto &y) -> decltype(x << y) { return x << y; }), Stream &, U const &>)
|
||||
str << value;
|
||||
else
|
||||
str << std::make_unsigned_t<U>(value);
|
||||
}
|
||||
|
||||
public:
|
||||
@ -830,7 +819,15 @@ public:
|
||||
str << reinterpret_cast<void const *>(std::uintptr_t(value));
|
||||
break;
|
||||
default:
|
||||
str << value;
|
||||
if constexpr (std::is_invocable_v<decltype([] (auto &x, auto &y) -> decltype(x << y) { return x << y; }), Stream &, U const &>)
|
||||
{
|
||||
str << value;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false); // stream out operator not declared or declared deleted
|
||||
str << '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
Loading…
Reference in New Issue
Block a user