Skip to content

Commit b12185b

Browse files
yuxuanchen1997github-actions[bot]
authored andcommitted
Automerge: [libcxx] Implement C++20 std::chrono::is_clock, std::chrono::is_clock_v (#160607)
Implemented [[*time.traits.is.clock*]](https://eel.is/c++draft/time.traits.is.clock) from [P0355R7](https://wg21.link/p0355r7). This patch implements the C++20 feature `is_clock` and `is_clock_v` based on the documentation [on cppreference](https://en.cppreference.com/w/cpp/chrono/is_clock.html) Fixes #166049.
2 parents bd357b0 + adc7932 commit b12185b

File tree

7 files changed

+332
-2
lines changed

7 files changed

+332
-2
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ set(files
262262
__chrono/gps_clock.h
263263
__chrono/hh_mm_ss.h
264264
__chrono/high_resolution_clock.h
265+
__chrono/is_clock.h
265266
__chrono/leap_second.h
266267
__chrono/literals.h
267268
__chrono/local_info.h

libcxx/include/__chrono/is_clock.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef _LIBCPP___CHRONO_IS_CLOCK_H
11+
#define _LIBCPP___CHRONO_IS_CLOCK_H
12+
13+
#include <__config>
14+
15+
#include <__chrono/duration.h>
16+
#include <__chrono/time_point.h>
17+
#include <__concepts/same_as.h>
18+
#include <__type_traits/integral_constant.h>
19+
#include <__type_traits/is_arithmetic.h>
20+
#include <__type_traits/is_class.h>
21+
#include <__type_traits/is_union.h>
22+
#include <ratio>
23+
24+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
25+
# pragma GCC system_header
26+
#endif
27+
28+
#if _LIBCPP_STD_VER >= 20
29+
30+
_LIBCPP_BEGIN_NAMESPACE_STD
31+
32+
namespace chrono {
33+
34+
// Helper to check that _Tp::time_point has the form time_point<_, typename _Tp::duration>.
35+
template <class _TimePoint, class _ClockType>
36+
inline constexpr bool __is_valid_clock_time_point_v = false;
37+
38+
template <class _TimePointClock, class _ClockType>
39+
inline constexpr bool
40+
__is_valid_clock_time_point_v<time_point<_TimePointClock, typename _ClockType::duration>, _ClockType> = true;
41+
42+
// Check if a clock satisfies the Cpp17Clock requirements as defined in [time.clock.req]
43+
template <class _Tp>
44+
_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_clock_v = requires {
45+
typename _Tp::rep;
46+
requires is_arithmetic_v<typename _Tp::rep> || is_class_v<typename _Tp::rep> || is_union_v<typename _Tp::rep>;
47+
48+
typename _Tp::period;
49+
requires __is_ratio_v<typename _Tp::period>;
50+
51+
typename _Tp::duration;
52+
requires same_as<typename _Tp::duration, duration<typename _Tp::rep, typename _Tp::period>>;
53+
54+
typename _Tp::time_point;
55+
requires __is_valid_clock_time_point_v<typename _Tp::time_point, _Tp>;
56+
57+
_Tp::is_steady;
58+
requires same_as<decltype((_Tp::is_steady)), const bool&>;
59+
60+
_Tp::now();
61+
requires same_as<decltype(_Tp::now()), typename _Tp::time_point>;
62+
};
63+
64+
template <class _Tp>
65+
struct _LIBCPP_NO_SPECIALIZATIONS is_clock : bool_constant<is_clock_v<_Tp>> {};
66+
67+
} // namespace chrono
68+
69+
_LIBCPP_END_NAMESPACE_STD
70+
71+
#endif // _LIBCPP_STD_VER
72+
#endif // _LIBCPP___CHRONO_IS_CLOCK_H

libcxx/include/chrono

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ template <class ToDuration, class Rep, class Period>
218218
template <class ToDuration, class Rep, class Period>
219219
constexpr ToDuration round(const duration<Rep, Period>& d); // C++17
220220
221+
template <class T> struct is_clock; // C++20
222+
template <class T> inline constexpr bool is_clock_v = is_clock<T>::value; // C++20
223+
221224
// duration I/O
222225
template<class charT, class traits, class Rep, class Period> // C++20
223226
basic_ostream<charT, traits>&
@@ -1057,6 +1060,7 @@ constexpr chrono::year operator ""y(unsigned lo
10571060
# include <__chrono/day.h>
10581061
# include <__chrono/exception.h>
10591062
# include <__chrono/hh_mm_ss.h>
1063+
# include <__chrono/is_clock.h>
10601064
# include <__chrono/literals.h>
10611065
# include <__chrono/local_info.h>
10621066
# include <__chrono/month.h>

libcxx/include/module.modulemap.in

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,10 @@ module std [system] {
973973
header "__chrono/high_resolution_clock.h"
974974
export *
975975
}
976+
module is_clock {
977+
header "__chrono/is_clock.h"
978+
export std_core.type_traits.integral_constant
979+
}
976980
module leap_second {
977981
header "__chrono/leap_second.h"
978982
}

libcxx/modules/std/chrono.inc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ export namespace std {
2525

2626
using std::chrono::duration_values;
2727

28-
// using std::chrono::is_clock;
29-
// using std::chrono::is_clock_v;
28+
using std::chrono::is_clock;
29+
using std::chrono::is_clock_v;
3030

3131
// [time.duration.nonmember], duration arithmetic
3232
using std::chrono::operator+;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// REQUIRES: std-at-least-c++20
10+
11+
#include <chrono>
12+
#include <ratio>
13+
14+
#if !__has_warning("-Winvalid-specializations")
15+
// expected-no-diagnostics
16+
#else
17+
18+
template <>
19+
struct std::chrono::is_clock<int> : std::false_type {}; // expected-error@*:* {{'is_clock' cannot be specialized}}
20+
21+
template <>
22+
constexpr bool std::chrono::is_clock_v<float> = false; // expected-error@*:* {{'is_clock_v' cannot be specialized}}
23+
24+
#endif
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// REQUIRES: std-at-least-c++20
10+
11+
#include <chrono>
12+
#include <ratio>
13+
14+
#include "test_macros.h"
15+
16+
struct EmptyStruct {};
17+
18+
// Test structs missing required members
19+
struct MissingRep {
20+
using period = std::ratio<1>;
21+
using duration = std::chrono::seconds;
22+
using time_point = std::chrono::time_point<MissingRep>;
23+
static constexpr bool is_steady = false;
24+
static time_point now();
25+
};
26+
27+
struct MissingPeriod {
28+
using rep = long;
29+
using duration = std::chrono::seconds;
30+
using time_point = std::chrono::time_point<MissingPeriod>;
31+
static constexpr bool is_steady = false;
32+
static time_point now();
33+
};
34+
35+
struct MissingDuration {
36+
using rep = long;
37+
using time_point = long;
38+
static constexpr bool is_steady = false;
39+
static time_point now();
40+
};
41+
42+
struct MissingTimePoint {
43+
using rep = long;
44+
using period = std::ratio<1>;
45+
using duration = std::chrono::seconds;
46+
static constexpr bool is_steady = false;
47+
static std::chrono::time_point<MissingTimePoint> now();
48+
};
49+
50+
struct MissingIsSteady {
51+
using rep = long;
52+
using period = std::ratio<1>;
53+
using duration = std::chrono::seconds;
54+
using time_point = std::chrono::time_point<MissingIsSteady>;
55+
static time_point now();
56+
};
57+
58+
struct MissingNow {
59+
using rep = long;
60+
using period = std::ratio<1>;
61+
using duration = std::chrono::seconds;
62+
using time_point = std::chrono::time_point<MissingNow>;
63+
static constexpr bool is_steady = false;
64+
};
65+
66+
// Valid clock types
67+
struct ValidSteadyClock {
68+
using rep = long long;
69+
using period = std::nano;
70+
using duration = std::chrono::nanoseconds;
71+
using time_point = std::chrono::time_point<ValidSteadyClock>;
72+
static constexpr bool is_steady = true;
73+
static time_point now();
74+
};
75+
76+
struct ValidSystemClock {
77+
using rep = long long;
78+
using period = std::micro;
79+
using duration = std::chrono::microseconds;
80+
using time_point = std::chrono::time_point<ValidSystemClock>;
81+
static constexpr bool is_steady = false;
82+
static time_point now();
83+
};
84+
85+
// Test clocks with invalid is_steady type
86+
struct WrongIsSteadyType {
87+
using rep = long;
88+
using period = std::ratio<1>;
89+
using duration = std::chrono::seconds;
90+
using time_point = std::chrono::time_point<WrongIsSteadyType>;
91+
static bool is_steady; // Not const bool
92+
static time_point now();
93+
};
94+
95+
struct WrongIsSteadyNonBool {
96+
using rep = long;
97+
using period = std::ratio<1>;
98+
using duration = std::chrono::seconds;
99+
using time_point = std::chrono::time_point<WrongIsSteadyNonBool>;
100+
static constexpr int is_steady = 1; // Not bool
101+
static time_point now();
102+
};
103+
104+
// Test clocks with invalid now() return type
105+
struct WrongNowReturnType {
106+
using rep = long;
107+
using period = std::ratio<1>;
108+
using duration = std::chrono::seconds;
109+
using time_point = std::chrono::time_point<WrongNowReturnType>;
110+
static constexpr bool is_steady = false;
111+
static int now(); // Wrong return type
112+
};
113+
114+
// Test clocks with invalid period type
115+
struct WrongPeriodType {
116+
using rep = long;
117+
using period = int; // Not a ratio
118+
using duration = std::chrono::seconds;
119+
using time_point = std::chrono::time_point<WrongPeriodType>;
120+
static constexpr bool is_steady = false;
121+
static time_point now();
122+
};
123+
124+
// Test clocks with wrong duration type
125+
struct WrongDurationType {
126+
using rep = long;
127+
using period = std::ratio<1>;
128+
using duration = std::chrono::milliseconds; // Should be duration<long, ratio<1>>
129+
using time_point = std::chrono::time_point<WrongDurationType>;
130+
static constexpr bool is_steady = false;
131+
static time_point now();
132+
};
133+
134+
// Test clocks with wrong time_point type
135+
struct WrongTimePointType {
136+
using rep = long;
137+
using period = std::ratio<1>;
138+
using duration = std::chrono::duration<long, std::ratio<1>>;
139+
using time_point = int; // Not a time_point
140+
static constexpr bool is_steady = false;
141+
static time_point now();
142+
};
143+
144+
struct WrongTimePointClock {
145+
using rep = long;
146+
using period = std::ratio<1>;
147+
using duration = std::chrono::duration<long, std::ratio<1>>;
148+
using time_point = std::chrono::time_point<ValidSystemClock>; // Wrong clock type
149+
static constexpr bool is_steady = false;
150+
static time_point now();
151+
};
152+
153+
// Valid clock with time_point that has matching duration instead of matching clock
154+
struct ValidClockWithDurationMatch {
155+
using rep = int;
156+
using period = std::milli;
157+
using duration = std::chrono::duration<int, std::milli>;
158+
using time_point = std::chrono::time_point<ValidSystemClock, duration>; // Valid: matches duration
159+
static constexpr bool is_steady = false;
160+
static time_point now();
161+
};
162+
163+
// Test both is_clock and is_clock_v
164+
static_assert(std::chrono::is_clock<std::chrono::system_clock>::value);
165+
static_assert(std::chrono::is_clock_v<std::chrono::system_clock>);
166+
167+
// Test standard clock types
168+
static_assert(std::chrono::is_clock_v<std::chrono::system_clock>);
169+
static_assert(std::chrono::is_clock_v<std::chrono::high_resolution_clock>);
170+
171+
// Test non-clock types
172+
static_assert(!std::chrono::is_clock_v<EmptyStruct>);
173+
static_assert(!std::chrono::is_clock_v<int>);
174+
static_assert(!std::chrono::is_clock_v<void>);
175+
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock::time_point>);
176+
static_assert(!std::chrono::is_clock_v<std::chrono::seconds>);
177+
static_assert(!std::chrono::is_clock_v<std::chrono::milliseconds>);
178+
179+
// Test structs missing required members
180+
static_assert(!std::chrono::is_clock_v<MissingRep>);
181+
static_assert(!std::chrono::is_clock_v<MissingPeriod>);
182+
static_assert(!std::chrono::is_clock_v<MissingDuration>);
183+
static_assert(!std::chrono::is_clock_v<MissingTimePoint>);
184+
static_assert(!std::chrono::is_clock_v<MissingIsSteady>);
185+
static_assert(!std::chrono::is_clock_v<MissingNow>);
186+
187+
// Test valid custom clocks
188+
static_assert(std::chrono::is_clock_v<ValidSteadyClock>);
189+
static_assert(std::chrono::is_clock_v<ValidSystemClock>);
190+
static_assert(std::chrono::is_clock_v<ValidClockWithDurationMatch>);
191+
192+
// cv-qualified and reference types
193+
static_assert(std::chrono::is_clock_v<const std::chrono::system_clock>);
194+
static_assert(std::chrono::is_clock_v<volatile std::chrono::system_clock>);
195+
static_assert(std::chrono::is_clock_v<const volatile std::chrono::system_clock>);
196+
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock&>);
197+
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock&&>);
198+
static_assert(!std::chrono::is_clock_v<const std::chrono::system_clock&>);
199+
200+
// array and pointer types
201+
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock[]>);
202+
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock[10]>);
203+
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock*>);
204+
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock* const>);
205+
206+
// The Standard defined a minimum set of checks and allowed implementation to perform stricter checks. The following
207+
// static asserts are implementation specific and a conforming standard library implementation doesn't have to produce
208+
// the same outcome.
209+
210+
// Test clocks with invalid is_steady type
211+
LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongIsSteadyType>); // is_steady not const bool
212+
LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongIsSteadyNonBool>); // is_steady not bool type
213+
214+
// Test clocks with invalid now() return type
215+
LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongNowReturnType>); // now() doesn't return time_point
216+
217+
// Test clocks with invalid period type
218+
LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongPeriodType>); // period is not a ratio
219+
220+
// Test clocks with wrong duration type
221+
LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongDurationType>); // duration doesn't match duration<rep, period>
222+
223+
// Test clocks with wrong time_point type
224+
LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongTimePointType>); // time_point is not a time_point
225+
LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongTimePointClock>); // time_point has wrong clock and wrong duration

0 commit comments

Comments
 (0)