On Thu, Nov 20, 2025 at 7:36 PM Tomasz Kaminski <[email protected]> wrote:
> > > On Thu, Nov 20, 2025 at 6:37 PM Jonathan Wakely <[email protected]> > 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 <[email protected]> >> >Signed-off-by: Tomasz Kamiński <[email protected]> >> >Signed-off-by: Giuseppe D'Angelo <[email protected]> >> >--- >> >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. > Relevant comment from original PR: - [time.duration] says that "Rep shall be an arithmetic type or a class emulating an arithmetic type". Technically speaking, this means that const int is a valid Rep; but we can't use hash. I'm not sure if this is deliberate or not (cf. LWG951, LWG953), but I've decided to support it nonetheless. > >> >+ >> >+ 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 >> > >> > >> >>
