summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2021-09-18 01:09:23 +1000
committer Vas Crabb <vas@vastheman.com>2021-09-18 01:09:23 +1000
commitd1e68f25dd9ce56c623cfdd96425bad0fa35dcdb (patch)
treec8f7430c1ce45847001d493d8f7eee10a6b87ecf /src/lib/util
parent6e3adb71999938f3a05a5bae4f108c701af3e77a (diff)
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.
Diffstat (limited to 'src/lib/util')
-rw-r--r--src/lib/util/delegate.cpp5
-rw-r--r--src/lib/util/delegate.h244
-rw-r--r--src/lib/util/strformat.h37
3 files changed, 136 insertions, 150 deletions
diff --git a/src/lib/util/delegate.cpp b/src/lib/util/delegate.cpp
index 8c816928625..0fbc6e91f94 100644
--- a/src/lib/util/delegate.cpp
+++ b/src/lib/util/delegate.cpp
@@ -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
diff --git a/src/lib/util/delegate.h b/src/lib/util/delegate.h
index 6f2c1feb0a6..2ba0c97ba2f 100644
--- a/src/lib/util/delegate.h
+++ b/src/lib/util/delegate.h
@@ -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() { }
diff --git a/src/lib/util/strformat.h b/src/lib/util/strformat.h
index a3ba1d11261..4714b9f5e71 100644
--- a/src/lib/util/strformat.h
+++ b/src/lib/util/strformat.h
@@ -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