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. > > >+ > >+ 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 > > > > > >
