From 4c7ed6638546f0245df9eefd56cc91862fe92f54 Mon Sep 17 00:00:00 2001 From: Casper Jeukendrup <48658420+cbjeukendrup@users.noreply.github.com> Date: Mon, 22 Jun 2026 01:38:10 +0200 Subject: [PATCH 1/3] TextStream operator<<: make it work for any integral type size_t != uint64_t on macOS, despite being a 64-bit unsigned integer. Make it a template, so that all integer types work. However after that, it broke for std::int8_t, aka signed char, so added an explicit overload for that. --- framework/global/serialization/textstream.cpp | 61 +++++++------------ framework/global/serialization/textstream.h | 46 ++++++++++++-- 2 files changed, 64 insertions(+), 43 deletions(-) diff --git a/framework/global/serialization/textstream.cpp b/framework/global/serialization/textstream.cpp index 8573084dd4..e178579564 100644 --- a/framework/global/serialization/textstream.cpp +++ b/framework/global/serialization/textstream.cpp @@ -64,30 +64,41 @@ TextStream& TextStream::operator<<(char ch) return *this; } -TextStream& TextStream::operator<<(const int32_t val) +TextStream& TextStream::write_int32_t(int32_t val) { // ceil(log_10(2^31)) = 10 (+ sign) - std::array buf{}; - const auto [last, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), val); - IF_ASSERT_FAILED(ec == std::errc {}) { - return *this; - } + return writeInt<11>(val); +} - write(buf.data(), static_cast(last - buf.data())); +TextStream& TextStream::write_uint32_t(uint32_t val) +{ + // ceil(log_10(2^32)) = 10 + return writeInt<10>(val); +} - return *this; +TextStream& TextStream::write_int64_t(int64_t val) +{ + // ceil(log_10(2^63)) = 20 (+ sign) + return writeInt<21>(val); } -TextStream& TextStream::operator<<(const uint32_t val) +TextStream& TextStream::write_uint64_t(uint64_t val) { - // ceil(log_10(2^32)) = 10 - std::array buf{}; + // ceil(log_10(2^64)) = 20 + return writeInt<20>(val); +} + +template +TextStream& TextStream::writeInt(NonCharInteger auto val) +{ + std::array buf{}; const auto [last, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), val); IF_ASSERT_FAILED(ec == std::errc {}) { return *this; } write(buf.data(), static_cast(last - buf.data())); + return *this; } @@ -120,34 +131,6 @@ TextStream& TextStream::operator<<(double val) return *this; } -TextStream& TextStream::operator<<(const int64_t val) -{ - // ceil(log_10(2^63)) = 20 (+ sign) - std::array buf{}; - const auto [last, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), val); - IF_ASSERT_FAILED(ec == std::errc {}) { - return *this; - } - - write(buf.data(), static_cast(last - buf.data())); - - return *this; -} - -TextStream& TextStream::operator<<(const uint64_t val) -{ - // ceil(log_10(2^64)) = 20 - std::array buf{}; - const auto [last, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), val); - IF_ASSERT_FAILED(ec == std::errc {}) { - return *this; - } - - write(buf.data(), static_cast(last - buf.data())); - - return *this; -} - TextStream& TextStream::operator<<(const char* s) { return operator<<(std::string_view { s }); diff --git a/framework/global/serialization/textstream.h b/framework/global/serialization/textstream.h index f88f2b88f0..4d019c380c 100644 --- a/framework/global/serialization/textstream.h +++ b/framework/global/serialization/textstream.h @@ -21,6 +21,7 @@ */ #pragma once +#include #include #include #include @@ -37,6 +38,17 @@ namespace io { class IODevice; } +template +concept NonCharInteger + =std::integral + && !std::same_as, char> + && !std::same_as, signed char> + && !std::same_as, unsigned char> + && !std::same_as, wchar_t> + && !std::same_as, char8_t> + && !std::same_as, char16_t> + && !std::same_as, char32_t>; + class TextStream { public: @@ -49,11 +61,28 @@ class TextStream void flush(); TextStream& operator<<(char ch); - TextStream& operator<<(int32_t); - TextStream& operator<<(uint32_t); + TextStream& operator<<(signed char ch) { return *this << static_cast(ch); } + TextStream& operator<<(unsigned char ch) { return *this << static_cast(ch); } + + TextStream& operator<<(NonCharInteger auto val) + { + if constexpr (sizeof(val) <= 4) { + if constexpr (std::is_signed_v) { + return write_int32_t(static_cast(val)); + } else { + return write_uint32_t(static_cast(val)); + } + } else { + if constexpr (std::is_signed_v) { + return write_int64_t(static_cast(val)); + } else { + return write_uint64_t(static_cast(val)); + } + } + } + TextStream& operator<<(double); - TextStream& operator<<(int64_t); - TextStream& operator<<(uint64_t); + TextStream& operator<<(const char* s); TextStream& operator<<(std::string_view); TextStream& operator<<(const ByteArray& b); @@ -65,6 +94,15 @@ class TextStream private: void write(const char* ch, size_t len); + + TextStream& write_int32_t(int32_t val); + TextStream& write_uint32_t(uint32_t val); + TextStream& write_int64_t(int64_t val); + TextStream& write_uint64_t(uint64_t val); + + template + TextStream& writeInt(NonCharInteger auto val); + io::IODevice* m_device = nullptr; std::vector m_buf; }; From cb22bcbc39519264242d8c436ed9bada2af69f8c Mon Sep 17 00:00:00 2001 From: Casper Jeukendrup <48658420+cbjeukendrup@users.noreply.github.com> Date: Thu, 25 Jun 2026 14:02:08 +0200 Subject: [PATCH 2/3] We're not ready for concepts yet --- framework/global/serialization/textstream.cpp | 4 +-- framework/global/serialization/textstream.h | 33 ++++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/framework/global/serialization/textstream.cpp b/framework/global/serialization/textstream.cpp index e178579564..418ea33141 100644 --- a/framework/global/serialization/textstream.cpp +++ b/framework/global/serialization/textstream.cpp @@ -88,8 +88,8 @@ TextStream& TextStream::write_uint64_t(uint64_t val) return writeInt<20>(val); } -template -TextStream& TextStream::writeInt(NonCharInteger auto val) +template +TextStream& TextStream::writeInt(T val) { std::array buf{}; const auto [last, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), val); diff --git a/framework/global/serialization/textstream.h b/framework/global/serialization/textstream.h index 4d019c380c..0a1cc5637e 100644 --- a/framework/global/serialization/textstream.h +++ b/framework/global/serialization/textstream.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #ifndef NO_QT_SUPPORT @@ -39,15 +40,16 @@ class IODevice; } template -concept NonCharInteger - =std::integral - && !std::same_as, char> - && !std::same_as, signed char> - && !std::same_as, unsigned char> - && !std::same_as, wchar_t> - && !std::same_as, char8_t> - && !std::same_as, char16_t> - && !std::same_as, char32_t>; +struct IsNonCharInteger : std::bool_constant< + std::is_integral_v + && !std::is_same_v, char> + && !std::is_same_v, signed char> + && !std::is_same_v, unsigned char> + && !std::is_same_v, wchar_t> + && !std::is_same_v, char8_t> + && !std::is_same_v, char16_t> + && !std::is_same_v, char32_t> + > {}; class TextStream { @@ -64,16 +66,17 @@ class TextStream TextStream& operator<<(signed char ch) { return *this << static_cast(ch); } TextStream& operator<<(unsigned char ch) { return *this << static_cast(ch); } - TextStream& operator<<(NonCharInteger auto val) + template::value, int> = 0> + TextStream& operator<<(T val) { - if constexpr (sizeof(val) <= 4) { - if constexpr (std::is_signed_v) { + if constexpr (sizeof(T) <= 4) { + if constexpr (std::is_signed_v) { return write_int32_t(static_cast(val)); } else { return write_uint32_t(static_cast(val)); } } else { - if constexpr (std::is_signed_v) { + if constexpr (std::is_signed_v) { return write_int64_t(static_cast(val)); } else { return write_uint64_t(static_cast(val)); @@ -100,8 +103,8 @@ class TextStream TextStream& write_int64_t(int64_t val); TextStream& write_uint64_t(uint64_t val); - template - TextStream& writeInt(NonCharInteger auto val); + template + TextStream& writeInt(T val); io::IODevice* m_device = nullptr; std::vector m_buf; From 58dd93d91d772f871a917ea8cd438f6e2b07bd4b Mon Sep 17 00:00:00 2001 From: Casper Jeukendrup <48658420+cbjeukendrup@users.noreply.github.com> Date: Thu, 25 Jun 2026 14:08:55 +0200 Subject: [PATCH 3/3] Eliminate helpers --- framework/global/serialization/textstream.cpp | 29 ++++--------------- framework/global/serialization/textstream.h | 13 +++------ 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/framework/global/serialization/textstream.cpp b/framework/global/serialization/textstream.cpp index 418ea33141..75fb75fd7e 100644 --- a/framework/global/serialization/textstream.cpp +++ b/framework/global/serialization/textstream.cpp @@ -64,30 +64,6 @@ TextStream& TextStream::operator<<(char ch) return *this; } -TextStream& TextStream::write_int32_t(int32_t val) -{ - // ceil(log_10(2^31)) = 10 (+ sign) - return writeInt<11>(val); -} - -TextStream& TextStream::write_uint32_t(uint32_t val) -{ - // ceil(log_10(2^32)) = 10 - return writeInt<10>(val); -} - -TextStream& TextStream::write_int64_t(int64_t val) -{ - // ceil(log_10(2^63)) = 20 (+ sign) - return writeInt<21>(val); -} - -TextStream& TextStream::write_uint64_t(uint64_t val) -{ - // ceil(log_10(2^64)) = 20 - return writeInt<20>(val); -} - template TextStream& TextStream::writeInt(T val) { @@ -102,6 +78,11 @@ TextStream& TextStream::writeInt(T val) return *this; } +template TextStream& TextStream::writeInt<11, int32_t>(int32_t val); +template TextStream& TextStream::writeInt<10, uint32_t>(uint32_t val); +template TextStream& TextStream::writeInt<21, int64_t>(int64_t val); +template TextStream& TextStream::writeInt<20, uint64_t>(uint64_t val); + TextStream& TextStream::operator<<(double val) { // macOS: requires macOS 13.3 at runtime diff --git a/framework/global/serialization/textstream.h b/framework/global/serialization/textstream.h index 0a1cc5637e..61b57b8b8d 100644 --- a/framework/global/serialization/textstream.h +++ b/framework/global/serialization/textstream.h @@ -71,15 +71,15 @@ class TextStream { if constexpr (sizeof(T) <= 4) { if constexpr (std::is_signed_v) { - return write_int32_t(static_cast(val)); + return writeInt<11>(static_cast(val)); // ceil(log_10(2^31)) = 10 (+ sign) } else { - return write_uint32_t(static_cast(val)); + return writeInt<10>(static_cast(val)); // ceil(log_10(2^32)) = 10 } } else { if constexpr (std::is_signed_v) { - return write_int64_t(static_cast(val)); + return writeInt<21>(static_cast(val)); // ceil(log_10(2^63)) = 20 (+ sign) } else { - return write_uint64_t(static_cast(val)); + return writeInt<20>(static_cast(val)); // ceil(log_10(2^64)) = 20 } } } @@ -98,11 +98,6 @@ class TextStream private: void write(const char* ch, size_t len); - TextStream& write_int32_t(int32_t val); - TextStream& write_uint32_t(uint32_t val); - TextStream& write_int64_t(int64_t val); - TextStream& write_uint64_t(uint64_t val); - template TextStream& writeInt(T val);