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

Reply via email to