On Thu, Nov 20, 2025 at 9:20 AM Tomasz Kaminski <[email protected]> wrote:
> > > On Wed, Nov 19, 2025 at 1:50 PM Tomasz Kamiński <[email protected]> > 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]> >> --- >> This is based on initial patch from Giuseppe that can be found here: >> https://forge.sourceware.org/gcc/gcc-TEST/pulls/52, however I have >> changed the >> implementation extensively. >> >> Tested on x86_64-linux locally. Additionally std/time/hash.cc tested >> with all supported standards and both COW (-D_GLIBCXX_USE_CXX11_ABI=0) >> and SSO strings. 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..249f4a9ff93 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 _T2, typename... _Ts> >> + [[__gnu__::__always_inline__]] >> + constexpr auto >> + __pack_ints(_T1 __v1, _T2 __v2, _Ts... __vrs) >> + { >> + if constexpr (sizeof...(_Ts) >= 1) >> + return __pack_ints(__pack_ints(__v1, __v2), __vrs...); >> > The order of packing here is important, what I mean, that if we pack > year (2B), month (1B), day (1B) in that order, I will produce: > UINT32 (4B), day(1B), and then UINT64 (8B). Going in reverse wil give: > year (2B), UINT16(2B) and UINT32(4B). > > I will revert this part to original code, that I changed just before > submission: > __pack_ints(__v1, __pack_ints(__v2, __vrs...)); > And have added a comment regarding this being order sensitive. > > I could implement full blown order independent optimal packing, but this > function is in namespace chrono, and used only here, so I think we > can rely on them being passed in "right" order. I.e. doing some fancy > trick would overengineer and would just increase compile time. > > Will post v2 with that fixed soon. > As it usually happens during lunch break, I realized that there is simple solution, that produces result of same size regardless of the order. template<typename _T1, typename... _Ts> [[__gnu__::__always_inline__]] constexpr auto __pack_ints(_T1 __v1, _Ts... __vrs) { using _ResT = ...; _ResT __res = v1; ((__res = ((__res << sizeof(_Ts) * __CHAR_BITS__) | _Res(__vsr), ....); return __res; } There will be v3. > > Regards, > Tomasz > > >> + else >> + { >> + using _ResT = decltype([] { >> + constexpr size_t __tsize = sizeof(_T1) + sizeof(_T2); >> + 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); >> + }()); >> + return _ResT((_ResT(__v1) << (sizeof(_T2) * __CHAR_BIT__)) | >> _ResT(__v2)); >> + } >> + } >> + >> + 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 >> + __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>; >> + >> + 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()); >> + } >> + }; >> + >> + 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 >> >>
