[PATCH v3] libstdc++: Hashing support for chrono value classes [PR110357]
Tomasz Kaminski
tkaminsk@redhat.com
Thu Nov 20 18:36:35 GMT 2025
On Thu, Nov 20, 2025 at 6:37 PM Jonathan Wakely <jwakely@redhat.com> wrote:
> On Thu, 20 Nov 2025 at 14:28 +0100, Tomasz Kamiński wrote:
> >This patch implements P2592R3 Hashing support for std::chrono value
> classes.
> >
> >To avoid the know issues with current hashing of integer types (see
> PR104945),
> >we use chrono::__int_hash function that hash the bytes of representation,
> >instead of hash<T>, as the later simply cast to value. Currently
> _Hash_impl
> >it used, but we should consider replacing it (see PR55815) before C++26
> ABI
> >is made stable. The function is declared inside <chrono> header and chrono
> >namespace, to make sure that only chrono components would be affected by
> >such change. Finally, chrono::__int_hash is made variadic, to support
> >combining hashes of multiple integers.
> >
> >To reduce the number of calls to hasher (defined out of line), the
> calendar
> >types are packed into single unsigned integer value. This is done by
> >chrono::__hash helper, that calls:
> >* chrono::__as_int to cast the value of single component, to unsigned
> integer
> > with size matching the one used by internal representation: unsigned
> short
> > for year/weekday_indexed, and unsigned char in all other cases.
> >* chrono::__pack_ints to pack integers (if more than one) into single
> integer
> > by performing bit shift operations
> >* chrono::__int_hash to hash the value produced by above.
> >
> >For type chrono::duration, __int_hash is used when the representation is
> >integral type, and for other types (floating point due special handling
> >of +/-0.0 and user defined types) we delegate to hash specialization.
> >This is automatically picked up by time_point, that delegates to hasher
> >of duration. Similarly for leap_second that is specified to use integer
> >durations, we simply hash representations of date() and value(). Finally
> >zoned_time in addition to handling integer durations as described above,
> >we also use __int_hash for const time_zone* (if used), as hash<T*> have
> >similar problems as hash specialization for integers. This is limited
> >only to _TimeZonePtr being const time_zone* (default), as user can define
> >hash specializations for raw pointers to they zones.
> >
> >As accessing the representation for duration requires calling count()
> >method that returns a copy of representation by value, the noexcept
> >specification of the hasher needs to take into consideration copy
> >constructor of duration. Similar reasoning applies for time_since_epoch
> >for time_points, and get_sys_time, get_time_zone for zoned_time.
> >For all this cases we use internal __is_nothrow_copy_hashable concept.
> >
> >Finally support for zoned_time is provided only for CXX11 string ABI,
> >__cpp_lib_chrono feature test macro cannot be bumped if COW string are
> used.
> >To indicate presence of hasher for remaining types this patch also bumps
> >the internal __glibcxx_chrono_cxx20 macro, and uses it as guard to new
> >features.
> >
> >libstdc++-v3/ChangeLog:
> >
> > * include/bits/version.def (chrono, chrono_cxx20): Bump values.
> > * include/bits/version.h: Regenerate.
> > * include/std/chrono (__is_nothrow_copy_hashable)
> > (chrono::__pack_ints, chrono::__as_int, chrono::__int_hash)
> > (chrono::__hash): Define.
> > (std::hash): Define partial specialization for duration,
> time_point,
> > and zoned_time, and full specializations for calendar types and
> > leap_second.
> > (std::__is_fast_hash): Define partial specializations for duration,
> > time_point, zoned_time.
> > * testsuite/std/time/hash.cc: New test.
> >
> >Co-authored-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
> >Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
> >Signed-off-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
> >---
> >v3 reimplements __pack_ints so the size of produced type,
> >is not dependend on order of arguments.
> >
> >Tested on x86_64-linux. Additionally std/time/hash.cc tested with
> >all standard modes and both string ABIs. OK for trunk?
> >
> > libstdc++-v3/include/bits/version.def | 13 +-
> > libstdc++-v3/include/bits/version.h | 13 +-
> > libstdc++-v3/include/std/chrono | 328 ++++++++++++++++++++++++
> > libstdc++-v3/testsuite/std/time/hash.cc | 281 ++++++++++++++++++++
> > 4 files changed, 632 insertions(+), 3 deletions(-)
> > create mode 100644 libstdc++-v3/testsuite/std/time/hash.cc
> >
> >diff --git a/libstdc++-v3/include/bits/version.def
> b/libstdc++-v3/include/bits/version.def
> >index 29ecf15c7e3..1fde9eef9d3 100644
> >--- a/libstdc++-v3/include/bits/version.def
> >+++ b/libstdc++-v3/include/bits/version.def
> >@@ -593,6 +593,12 @@ ftms = {
> >
> > ftms = {
> > name = chrono;
> >+ values = {
> >+ v = 202306;
> >+ cxxmin = 26;
> >+ hosted = yes;
> >+ cxx11abi = yes;
> >+ };
> > values = {
> > v = 201907;
> > cxxmin = 20;
> >@@ -607,8 +613,13 @@ ftms = {
> > };
> >
> > ftms = {
> >- // Unofficial macro for C++20 chrono features supported for old string
> ABI.
> >+ // Unofficial macro for chrono features supported for old string ABI.
> > name = chrono_cxx20;
> >+ values = {
> >+ v = 202306;
> >+ cxxmin = 26;
> >+ no_stdname = yes;
> >+ };
> > values = {
> > v = 201800;
> > cxxmin = 20;
> >diff --git a/libstdc++-v3/include/bits/version.h
> b/libstdc++-v3/include/bits/version.h
> >index 5901d27113d..2ebc48b234b 100644
> >--- a/libstdc++-v3/include/bits/version.h
> >+++ b/libstdc++-v3/include/bits/version.h
> >@@ -666,7 +666,12 @@
> > #undef __glibcxx_want_boyer_moore_searcher
> >
> > #if !defined(__cpp_lib_chrono)
> >-# if (__cplusplus >= 202002L) && _GLIBCXX_USE_CXX11_ABI &&
> _GLIBCXX_HOSTED
> >+# if (__cplusplus > 202302L) && _GLIBCXX_USE_CXX11_ABI &&
> _GLIBCXX_HOSTED
> >+# define __glibcxx_chrono 202306L
> >+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_chrono)
> >+# define __cpp_lib_chrono 202306L
> >+# endif
> >+# elif (__cplusplus >= 202002L) && _GLIBCXX_USE_CXX11_ABI &&
> _GLIBCXX_HOSTED
> > # define __glibcxx_chrono 201907L
> > # if defined(__glibcxx_want_all) || defined(__glibcxx_want_chrono)
> > # define __cpp_lib_chrono 201907L
> >@@ -681,7 +686,11 @@
> > #undef __glibcxx_want_chrono
> >
> > #if !defined(__cpp_lib_chrono_cxx20)
> >-# if (__cplusplus >= 202002L)
> >+# if (__cplusplus > 202302L)
> >+# define __glibcxx_chrono_cxx20 202306L
> >+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_chrono_cxx20)
> >+# endif
> >+# elif (__cplusplus >= 202002L)
> > # define __glibcxx_chrono_cxx20 201800L
> > # if defined(__glibcxx_want_all) || defined(__glibcxx_want_chrono_cxx20)
> > # endif
> >diff --git a/libstdc++-v3/include/std/chrono
> b/libstdc++-v3/include/std/chrono
> >index f0207eaae8e..b25e9979254 100644
> >--- a/libstdc++-v3/include/std/chrono
> >+++ b/libstdc++-v3/include/std/chrono
> >@@ -64,6 +64,9 @@
> > # include <bits/shared_ptr.h>
> > # include <bits/unique_ptr.h>
> > #endif
> >+#if __glibcxx_chrono_cxx20 >= 202306L // C++26
> >+# include <bits/functional_hash.h>
> >+#endif
> >
> > namespace std _GLIBCXX_VISIBILITY(default)
> > {
> >@@ -3322,6 +3325,331 @@ namespace __detail
> > #endif // C++20
> > } // namespace chrono
> >
> >+#if __glibcxx_chrono_cxx20 >= 202306 // C++26
> >+ // Hash support [time.hash]
> >+
> >+ template<typename _Tp, typename _Td = remove_cv_t<_Tp>>
> >+ concept __is_nothrow_copy_hashable = requires(const _Td& __t) {
> >+ { hash<_Td>{}(_Td(__t)) } noexcept -> same_as<size_t>;
> >+ };
> >+
> >+ namespace chrono {
> >+
> >+ template<typename _T1, typename... _Ts>
> >+ [[__gnu__::__always_inline__]]
> >+ constexpr auto
> >+ __pack_ints(_T1 __v1, _Ts... __vs)
> >+ {
> >+ using _ResT = decltype([] {
> >+ constexpr size_t __tsize = (sizeof(_T1) + ... + sizeof(_Ts));
> >+ if constexpr (__tsize <= 1)
> >+ return static_cast<unsigned char>(0);
> >+ else if constexpr (__tsize <= 2)
> >+ return static_cast<__UINT16_TYPE__>(0);
> >+ else if constexpr (__tsize <= 4)
> >+ return static_cast<__UINT32_TYPE__>(0);
> >+ else if constexpr (__tsize <= 8)
> >+ return static_cast<__UINT64_TYPE__>(0);
> >+ else
> >+ static_assert(__tsize <= 8);
> >+ }());
>
> Does this mean std::chrono::duration<__int128> can't be hashed?
>
No, durations representations are not packed, they go directly to
__int_hash,
and not chrono::__hash
There would be no point of doing so. We pack only calendar types.
>
> >+
> >+ _ResT __res = __v1;
> >+ ((__res = (__res <= (sizeof(_Ts) * __CHAR_BIT__) | _ResT(__vs))),
> ...);
> >+ return __res;
> >+ }
> >+
> >+ template<typename _Tp>
> >+ [[__gnu__::__always_inline__]]
> >+ constexpr auto
> >+ __as_int(_Tp __val)
> >+ {
> >+ if constexpr (is_same_v<_Tp, year>)
> >+ return static_cast<unsigned short>(static_cast<int>(__val));
> >+ else if constexpr (is_same_v<_Tp, month> || is_same_v<_Tp, day>)
> >+ return static_cast<unsigned char>(static_cast<unsigned>(__val));
> >+ else if constexpr (is_same_v<_Tp, weekday>)
> >+ return static_cast<unsigned char>(__val.c_encoding());
> >+ else if constexpr (is_same_v<_Tp, weekday_indexed>)
> >+ return __pack_ints(chrono::__as_int(__val.weekday()),
> >+ static_cast<unsigned char>(__val.index()));
> >+ else if constexpr (is_same_v<_Tp, weekday_last>)
> >+ return chrono::__as_int(__val.weekday());
> >+ else
> >+ static_assert(false);
> >+ }
> >+
> >+ template<typename _Arg, typename... _Args>
> >+ size_t
>
> Is this intentionally not 'inline' ?
>
Yes, other functions are very thin wrappers over value, this one contains
out of
line calls.
>
> >+ __int_hash(_Arg __arg, _Args... __args)
> >+ {
> >+ static_assert((is_integral_v<_Arg> && ... &&
> is_integral_v<_Args>));
> >+
> >+ // TODO consider using a better quality hasher
> >+ using _Hasher = _Hash_impl;
> >+ size_t __result = _Hasher::hash(__arg);
> >+ ((__result = _Hasher::__hash_combine(__args, __result)), ...);
> >+ return __result;
> >+ }
> >+
> >+ template<typename... _Tps>
> >+ [[__gnu__::__always_inline__]]
> >+ inline size_t
> >+ __hash(_Tps... __vals)
> >+ {
> >+ if constexpr (sizeof...(_Tps) == 1)
> >+ return chrono::__int_hash(chrono::__as_int(__vals)...);
> >+ else
> >+ {
> >+ auto __packed =
> chrono::__pack_ints(chrono::__as_int(__vals)...);
> >+ return chrono::__int_hash(__packed);
> >+ }
> >+ }
> >+ } // namespace chrono
> >+
> >+ // duration
> >+ template<typename _Rep, typename _Period>
> >+ requires __is_hash_enabled_for<remove_cv_t<_Rep>>
> >+ struct hash<chrono::duration<_Rep, _Period>>
> >+ {
> >+ private:
> >+ using _ActualRep = remove_cv_t<_Rep>;
>
> Do we allow duration<const int> ?!?
>
Giuseppe added them in the test, and they complied, so I guess we do.
>
> >+
> >+ public:
> >+ size_t
> >+ operator()(const chrono::duration<_Rep, _Period>& __val) const
> >+ noexcept(__is_nothrow_copy_hashable<_Rep>)
> >+ {
> >+ if constexpr (is_integral_v<_ActualRep>)
> >+ return chrono::__int_hash(__val.count());
> >+ else
> >+ return hash<_ActualRep>{}(__val.count());
> >+ }
>
> This definition means that 1s and 1ms and 1ns will hash to the same
> value. That's not necessarily a problem, but could be surprising.
>
> I think we did discuss it with Giuseppe, and I guess we decided not to
> hash the period, since that's what his original patches did? I think
> since you can't store 1ms and 1ns in the same std::unordered_xxx
> container, it doesn't matter. You'd need to convert them to a common
> type, and then 1'000'000ns and 1ns would hash to different values.
>
> If we wanted to hash the period, it would be quite easy, as we could
> just combine val.count() and Period::type::num and Period::type::den.
> Then people might be surprised that 1s and 1000ms don't hash the same
> though.
>
> So I think not hashing the period is probably the right answer.
>
> >+ };
> >+
> >+ template<typename _Rep, typename _Period>
> >+ struct __is_fast_hash<hash<chrono::duration<_Rep, _Period>>>
> >+ : __is_fast_hash<hash<remove_cv_t<_Rep>>>
> >+ {};
> >+
> >+ // time_point
> >+ template<typename _Clock, typename _Dur>
> >+ requires __is_hash_enabled_for<_Dur>
> >+ struct hash<chrono::time_point<_Clock, _Dur>>
> >+ {
> >+ size_t
> >+ operator()(const chrono::time_point<_Clock, _Dur>& __val) const
> >+ noexcept(__is_nothrow_copy_hashable<_Dur>)
> >+ { return hash<_Dur>{}(__val.time_since_epoch()); }
> >+ };
> >+
> >+ template<typename _Clock, typename _Dur>
> >+ struct __is_fast_hash<hash<chrono::time_point<_Clock, _Dur>>>
> >+ : __is_fast_hash<hash<_Dur>>
> >+ {};
> >+
> >+ // day
> >+ template<>
> >+ struct hash<chrono::day>
> >+ {
> >+ size_t
> >+ operator()(chrono::day __val) const noexcept
> >+ { return chrono::__hash(__val); }
> >+ };
> >+
> >+ // month
> >+ template<>
> >+ struct hash<chrono::month>
> >+ {
> >+ size_t
> >+ operator()(chrono::month __val) const noexcept
> >+ { return chrono::__hash(__val); }
> >+ };
> >+
> >+ // year
> >+ template<>
> >+ struct hash<chrono::year>
> >+ {
> >+ size_t
> >+ operator()(chrono::year __val) const noexcept
> >+ { return chrono::__hash(__val); }
> >+ };
> >+
> >+ // weekday
> >+ template<>
> >+ struct hash<chrono::weekday>
> >+ {
> >+ size_t
> >+ operator()(chrono::weekday __val) const noexcept
> >+ { return chrono::__hash(__val); }
> >+ };
> >+
> >+ // weekday_indexed
> >+ template<>
> >+ struct hash<chrono::weekday_indexed>
> >+ {
> >+ size_t
> >+ operator()(chrono::weekday_indexed __val) const noexcept
> >+ { return chrono::__hash(__val); }
> >+ };
> >+
> >+ // weekday_last
> >+ template<>
> >+ struct hash<chrono::weekday_last>
> >+ {
> >+ size_t
> >+ operator()(chrono::weekday_last __val) const noexcept
> >+ { return chrono::__hash(__val); }
> >+ };
> >+
> >+ // month_day
> >+ template<>
> >+ struct hash<chrono::month_day>
> >+ {
> >+ size_t
> >+ operator()(chrono::month_day __val) const noexcept
> >+ { return chrono::__hash(__val.month(), __val.day()); }
> >+ };
> >+
> >+ // month_day_last
> >+ template<>
> >+ struct hash<chrono::month_day_last>
> >+ {
> >+ size_t operator()(chrono::month_day_last __val) const noexcept
> >+ { return chrono::__hash(__val.month()); }
> >+ };
> >+
> >+ // month_weekday
> >+ template<>
> >+ struct hash<chrono::month_weekday>
> >+ {
> >+ size_t
> >+ operator()(chrono::month_weekday __val) const noexcept
> >+ { return chrono::__hash(__val.month(), __val.weekday_indexed()); }
> >+ };
> >+
> >+ // month_weekday_last
> >+ template<>
> >+ struct hash<chrono::month_weekday_last>
> >+ {
> >+ size_t
> >+ operator()(chrono::month_weekday_last __val) const noexcept
> >+ { return chrono::__hash(__val.month(), __val.weekday_last()); }
> >+ };
> >+
> >+ // year_month
> >+ template<>
> >+ struct hash<chrono::year_month>
> >+ {
> >+ size_t
> >+ operator()(chrono::year_month __val) const noexcept
> >+ { return chrono::__hash(__val.year(), __val.month()); }
> >+ };
> >+
> >+ // year_month_day
> >+ template<>
> >+ struct hash<chrono::year_month_day>
> >+ {
> >+ size_t
> >+ operator()(chrono::year_month_day __val) const noexcept
> >+ { return chrono::__hash(__val.year(), __val.month(), __val.day());
> }
> >+ };
> >+
> >+ // year_month_day_last
> >+ template<>
> >+ struct hash<chrono::year_month_day_last>
> >+ {
> >+ size_t
> >+ operator()(chrono::year_month_day_last __val) const noexcept
> >+ { return chrono::__hash(__val.year(), __val.month()); }
> >+ };
> >+
> >+ // year_month_weekday
> >+ template<>
> >+ struct hash<chrono::year_month_weekday>
> >+ {
> >+ size_t
> >+ operator()(chrono::year_month_weekday __val) const noexcept
> >+ {
> >+ return chrono::__hash(__val.year(), __val.month(),
> >+ __val.weekday_indexed());
> >+ }
> >+ };
> >+
> >+ // year_month_weekday_last
> >+ template<>
> >+ struct hash<chrono::year_month_weekday_last>
> >+ {
> >+ size_t
> >+ operator()(chrono::year_month_weekday_last __val) const noexcept
> >+ {
> >+ return chrono::__hash(__val.year(), __val.month(),
> >+ __val.weekday_last());
> >+ }
> >+ };
> >+
> >+#if _GLIBCXX_HOSTED
> >+#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
> >+ // zoned_time
> >+ template<typename _Duration, typename _TimeZonePtr>
> >+ requires __is_hash_enabled_for<
> >+ typename chrono::zoned_time<_Duration,
> _TimeZonePtr>::duration>
> >+ && __is_hash_enabled_for<_TimeZonePtr>
> >+ struct hash<chrono::zoned_time<_Duration, _TimeZonePtr>>
> >+ {
> >+ private:
> >+ using _ActualDuration =
> >+ typename chrono::zoned_time<_Duration, _TimeZonePtr>::duration;
> >+
> >+ public:
> >+ size_t
> >+ operator()(const chrono::zoned_time<_Duration, _TimeZonePtr>&
> __val) const
> >+ noexcept(__is_nothrow_copy_hashable<_ActualDuration>
> >+ && __is_nothrow_copy_hashable<_TimeZonePtr>)
> >+ {
> >+ const auto __iduration = [&] {
> >+ const _ActualDuration __sd =
> __val.get_sys_time().time_since_epoch();
> >+ if constexpr (is_integral_v<typename _ActualDuration::rep>)
> >+ return __sd.count();
> >+ else
> >+ return hash<_ActualDuration>{}(__sd);
> >+ }();
> >+
> >+ const auto __izone = [&] {
> >+ const _TimeZonePtr __tz = __val.get_time_zone();
> >+ if constexpr (is_same_v<_TimeZonePtr, const chrono::time_zone*>)
> >+ return reinterpret_cast<uintptr_t>(__tz);
> >+ else
> >+ return hash<_TimeZonePtr>{}(__tz);
> >+ }();
> >+
> >+ return chrono::__int_hash(__iduration, __izone);
> >+ }
> >+ };
> >+
> >+ template<typename _Duration, typename _TimeZonePtr>
> >+ struct __is_fast_hash<hash<chrono::zoned_time<_Duration,
> _TimeZonePtr>>>
> >+ : __and_<__is_fast_hash<hash<
> >+ typename chrono::zoned_time<_Duration,
> _TimeZonePtr>::duration>>,
> >+ __is_fast_hash<hash<_TimeZonePtr>>>
> >+ {};
> >+
> >+ // leap_second
> >+ template<>
> >+ struct hash<chrono::leap_second>
> >+ {
> >+ size_t
> >+ operator()(chrono::leap_second __val) const noexcept
> >+ {
> >+ return chrono::__int_hash(
> >+ __val.date().time_since_epoch().count(),
> >+ __val.value().count());
> >+ }
> >+ };
> >+#endif // _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
> >+#endif // _GLIBCXX_HOSTED
> >+#endif // __glibcxx_chrono_cxx20 >= 202306
> >+
> > #ifdef __glibcxx_chrono_cxx20
> > inline namespace literals
> > {
> >diff --git a/libstdc++-v3/testsuite/std/time/hash.cc
> b/libstdc++-v3/testsuite/std/time/hash.cc
> >new file mode 100644
> >index 00000000000..8d3b54c0931
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/std/time/hash.cc
> >@@ -0,0 +1,281 @@
> >+// { dg-do run { target c++26 } }
> >+
> >+#include <chrono>
> >+#include <unordered_set>
> >+#include <limits.h>
> >+#include <testsuite_hooks.h>
> >+
> >+#if _GLIBCXX_USE_CXX11_ABI
> >+# if !defined(__cpp_lib_chrono)
> >+# error "__cpp_lib_chrono not defined"
> >+# elif __cpp_lib_chrono < 202306L
> >+# error "Wrong value for __cpp_lib_chrono"
> >+# endif
> >+#endif
> >+
> >+template <typename T>
> >+struct arithmetic_wrapper
> >+{
> >+ arithmetic_wrapper() = default;
> >+ arithmetic_wrapper(T t) : t(t) {}
> >+ friend bool operator==(arithmetic_wrapper, arithmetic_wrapper) =
> default;
> >+ T t;
> >+};
> >+
> >+template <typename T>
> >+struct std::hash<arithmetic_wrapper<T>>
> >+{
> >+ size_t operator()(arithmetic_wrapper<T> val) const noexcept
> >+ { return std::hash<T>{}(val.t); }
> >+};
> >+
> >+template <typename T>
> >+struct non_hashable_arithmetic_wrapper
> >+{
> >+ non_hashable_arithmetic_wrapper() = default;
> >+ non_hashable_arithmetic_wrapper(T t) : t(t) {}
> >+ friend bool operator==(non_hashable_arithmetic_wrapper,
> non_hashable_arithmetic_wrapper) = default;
> >+ T t;
> >+};
> >+
> >+template <typename T>
> >+constexpr bool is_hash_poisoned =
> !std::is_default_constructible_v<std::hash<T>>;
> >+
> >+template <typename T>
> >+void test_unordered_set(const T& t)
> >+{
> >+ std::unordered_set<T> set;
> >+
> >+ set.insert(t);
> >+ VERIFY(set.size() == 1);
> >+ VERIFY(set.contains(t));
> >+
> >+ set.erase(t);
> >+ VERIFY(set.size() == 0);
> >+ VERIFY(!set.contains(t));
> >+}
> >+
> >+template <typename T>
> >+void test_hash(const T& t)
> >+{
> >+ static_assert(noexcept(std::hash<T>{}(t)));
> >+ static_assert(std::__is_fast_hash<T>::value);
> >+ test_unordered_set(t);
> >+}
> >+
> >+void test01()
> >+{
> >+ using namespace std::chrono;
> >+ using namespace std::literals::chrono_literals;
> >+
> >+ // duration
> >+ test_hash(-999s);
> >+ test_hash(1234ms);
> >+ test_hash(duration<double>(123.45));
> >+ using AWint = arithmetic_wrapper<int>;
> >+ test_hash(duration<AWint>(AWint(1234)));
> >+ using AWdouble = arithmetic_wrapper<double>;
> >+ test_hash(duration<AWdouble>(AWdouble(123.45)));
> >+
> >+ // time_point
> >+ test_hash(sys_seconds(1234s));
> >+ test_hash(sys_time<duration<double>>(duration<double>(123.45)));
> >+ test_hash(utc_seconds(1234s));
> >+ test_hash(local_days(days(1234)));
> >+ test_hash(system_clock::now());
> >+ test_hash(steady_clock::now());
> >+ test_hash(utc_clock::now());
> >+ test_hash(gps_clock::now());
> >+
> >+ // day
> >+ test_hash(1d);
> >+ test_hash(0d);
> >+ test_hash(255d);
> >+ test_hash(1234d);
> >+ test_hash(day(UINT_MAX));
> >+
> >+ // month
> >+ test_hash(January);
> >+ test_hash(September);
> >+ test_hash(month(0u));
> >+ test_hash(month(255u));
> >+ test_hash(month(1234u));
> >+ test_hash(month(UINT_MAX));
> >+
> >+ // year
> >+ test_hash(2024y);
> >+ test_hash(0y);
> >+ test_hash(year::min());
> >+ test_hash(year::max());
> >+ test_hash(year(INT_MAX));
> >+ test_hash(year(INT_MIN));
> >+
> >+ // weekday
> >+ test_hash(Monday);
> >+ test_hash(Thursday);
> >+ test_hash(weekday(255u));
> >+ test_hash(weekday(UINT_MAX));
> >+
> >+ // weekday_indexed
> >+ test_hash(Monday[0u]);
> >+ test_hash(Monday[7u]);
> >+ test_hash(Monday[1234u]);
> >+ test_hash(weekday(1234u)[0u]);
> >+
> >+ // weekday_last
> >+ test_hash(Monday[last]);
> >+ test_hash(Friday[last]);
> >+ test_hash(weekday(1234u)[last]);
> >+
> >+ // month_day
> >+ test_hash(March / 3);
> >+ test_hash(March / 31);
> >+ test_hash(February / 31);
> >+ test_hash(February / 1234);
> >+ test_hash(month(1234u) / 1);
> >+
> >+ // month_day_last
> >+ test_hash(March / last);
> >+ test_hash(month(1234u) / last);
> >+
> >+ // month_weekday
> >+ test_hash(March / Tuesday[2u]);
> >+ test_hash(month(1234u) / Tuesday[2u]);
> >+ test_hash(March / weekday(1234u)[2u]);
> >+ test_hash(March / Tuesday[1234u]);
> >+
> >+ // month_weekday_last
> >+ test_hash(April / Sunday[last]);
> >+ test_hash(month(1234u) / Tuesday[last]);
> >+ test_hash(April / weekday(1234u)[last]);
> >+
> >+ // year_month
> >+ test_hash(2024y / August);
> >+ test_hash(1'000'000y / August);
> >+ test_hash(2024y / month(1234u));
> >+
> >+ // year_month_day
> >+ test_hash(2024y / August / 31);
> >+ test_hash(-10y / March / 5);
> >+ test_hash(2024y / February / 31);
> >+ test_hash(1'000'000y / March / 5);
> >+ test_hash(2024y / month(1234u) / 5);
> >+ test_hash(2024y / March / 1234);
> >+
> >+ // year_month_day_last
> >+ test_hash(2024y / August / last);
> >+ test_hash(1'000'000y / August / last);
> >+ test_hash(2024y / month(1234u) / last);
> >+
> >+ // year_month_weekday
> >+ test_hash(2024y / August / Tuesday[2u]);
> >+ test_hash(-10y / August / Tuesday[2u]);
> >+ test_hash(1'000'000y / August / Tuesday[2u]);
> >+ test_hash(2024y / month(1234u) / Tuesday[2u]);
> >+ test_hash(2024y / August / weekday(1234u)[2u]);
> >+ test_hash(2024y / August / Tuesday[1234u]);
> >+
> >+ // year_month_weekday_last
> >+ test_hash(2024y / August / Tuesday[last]);
> >+ test_hash(-10y / August / Tuesday[last]);
> >+ test_hash(1'000'000y / August / Tuesday[last]);
> >+ test_hash(2024y / month(1234u) / Tuesday[last]);
> >+ test_hash(2024y / August / weekday(1234u)[last]);
> >+
> >+#if _GLIBCXX_USE_CXX11_ABI
> >+ // zoned_time
> >+ test_hash(zoned_seconds("Europe/Rome", sys_seconds(1234s)));
> >+ test_hash(zoned_time("Europe/Rome", system_clock::now()));
> >+
> >+ // leap_second
> >+ for (leap_second l : get_tzdb().leap_seconds)
> >+ test_hash(l);
> >+#endif
> >+}
> >+
> >+void test02()
> >+{
> >+ using namespace std::chrono;
> >+ using namespace std::literals::chrono_literals;
> >+
> >+ {
> >+ std::unordered_set<milliseconds> set;
> >+ set.insert(2000ms);
> >+ VERIFY(set.contains(2000ms));
> >+ VERIFY(set.contains(2s));
> >+ VERIFY(!set.contains(1234ms));
> >+ VERIFY(!set.contains(1234s));
> >+ }
> >+ {
> >+ using TP = sys_time<milliseconds>;
> >+ std::unordered_set<TP> set;
> >+ set.insert(TP(2000ms));
> >+ VERIFY(set.contains(TP(2000ms)));
> >+ VERIFY(set.contains(sys_seconds(2s)));
> >+ VERIFY(!set.contains(TP(1234ms)));
> >+ VERIFY(!set.contains(sys_seconds(1234s)));
> >+ }
> >+}
> >+
> >+void test03()
> >+{
> >+ using namespace std::chrono;
> >+
> >+ static constexpr
> >+ auto test_hash = []<typename T>(const T& t)
> >+ {
> >+ static_assert(noexcept(std::hash<T>{}(t)));
> >+ };
> >+
> >+ static constexpr
> >+ auto test = []<typename D>(const D& d)
> >+ {
> >+ test_hash(d);
> >+ test_hash(sys_time<D>(d));
> >+#if _GLIBCXX_USE_CXX11_ABI
> >+ test_hash(zoned_time<D>(sys_time<D>(d)));
> >+#endif
> >+ };
> >+
> >+
> >+ test(duration<int>(123));
> >+ test(duration<int, std::ratio<1, 1000>>(123));
> >+ test(duration<int, std::ratio<1000, 1>>(123));
> >+ test(duration<volatile double>(123.456));
> >+ test(duration<arithmetic_wrapper<int>>(arithmetic_wrapper<int>(123)));
> >+
> >+ test(duration<const int>(123));
> >+ test(duration<const int, std::ratio<1, 1000>>(123));
> >+ test(duration<const int, std::ratio<1000, 1>>(123));
> >+ test(duration<const volatile double>(123.456));
> >+ test(duration<const
> arithmetic_wrapper<int>>(arithmetic_wrapper<int>(123)));
> >+}
> >+
> >+void test04()
> >+{
> >+ using namespace std::chrono;
> >+
> >+ static_assert(!is_hash_poisoned<duration<int>>);
> >+ static_assert(!is_hash_poisoned<duration<double>>);
> >+ static_assert(!is_hash_poisoned<duration<arithmetic_wrapper<int>>>);
> >+ static_assert(!is_hash_poisoned<duration<arithmetic_wrapper<double>>>);
> >+
> static_assert(is_hash_poisoned<duration<non_hashable_arithmetic_wrapper<int>>>);
> >+
> static_assert(is_hash_poisoned<duration<non_hashable_arithmetic_wrapper<double>>>);
> >+
> >+#if _GLIBCXX_USE_CXX11_ABI
> >+ static_assert(!is_hash_poisoned<zoned_time<duration<int>>>);
> >+ static_assert(!is_hash_poisoned<zoned_time<duration<double>>>);
> >+
> static_assert(!is_hash_poisoned<zoned_time<duration<arithmetic_wrapper<int>>>>);
> >+
> static_assert(!is_hash_poisoned<zoned_time<duration<arithmetic_wrapper<double>>>>);
> >+
> static_assert(is_hash_poisoned<zoned_time<duration<non_hashable_arithmetic_wrapper<int>>>>);
> >+
> static_assert(is_hash_poisoned<zoned_time<duration<non_hashable_arithmetic_wrapper<double>>>>);
> >+#endif
> >+}
> >+
> >+int main()
> >+{
> >+ test01();
> >+ test02();
> >+ test03();
> >+ test04();
> >+}
> >--
> >2.51.1
> >
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20251120/3c085ed3/attachment-0001.htm>
More information about the Libstdc++
mailing list