Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ set(files
__chrono/gps_clock.h
__chrono/hh_mm_ss.h
__chrono/high_resolution_clock.h
__chrono/is_clock.h
__chrono/leap_second.h
__chrono/literals.h
__chrono/local_info.h
Expand Down
72 changes: 72 additions & 0 deletions libcxx/include/__chrono/is_clock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___CHRONO_IS_CLOCK_H
#define _LIBCPP___CHRONO_IS_CLOCK_H

#include <__config>
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_arithmetic.h>
#include <__type_traits/is_same.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

#if _LIBCPP_STD_VER >= 20

_LIBCPP_BEGIN_NAMESPACE_STD

namespace chrono {

template <class _Rep, class _Period>
class duration;

template <class _Clock, class _Duration>
class time_point;

template <class _TimePoint, class _Clock>
constexpr bool __is_valid_clock_time_point_v = false;

template <class _Clock, class _Duration, class _ClockType>
constexpr bool __is_valid_clock_time_point_v<time_point<_Clock, _Duration>, _ClockType> =
_IsSame<time_point<_Clock, _Duration>, time_point<_ClockType>>::value ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can manually add the template argument here:

Suggested change
_IsSame<time_point<_Clock, _Duration>, time_point<_ClockType>>::value ||
_IsSame<time_point<_Clock, _Duration>, time_point<_ClockType, typename _ClockType::duration>>::value ||

However, it seems simpler to check the relationship between _Tp::time_point and _Tp::duration. I.e. _Tp::time_point must be of form time_point<_Clock, typename _Tp::duration>.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about just change it to

requires _IsSame<typename _Tp::time_point::duration, typename _Tp::duration>::value;

?

I think it's the same. Just taking advantage the member type alias.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. They're not the same IIUC. We should also check that _Tp::time_point is a std::chrono::time_point specialization.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. They're not the same IIUC. We should also check that _Tp::time_point is a std::chrono::time_point specialization.

I have updated the patch to incorporate your suggestion "_Tp::time_point must be of form time_point<_Clock, typename _Tp::duration>."

This seems both correct and concise to me.

_IsSame<_Duration, typename _ClockType::duration>::value;

// Check if a clock satisfies the Cpp17Clock requirements as defined in [time.clock.req]
template <class _Tp>
_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_clock_v = requires {
typename _Tp::rep;
requires is_arithmetic_v<typename _Tp::rep>;

typename _Tp::period;
requires __is_ratio_v<typename _Tp::period>;

typename _Tp::duration;
requires _IsSame<typename _Tp::duration, duration<typename _Tp::rep, typename _Tp::period>>::value;

typename _Tp::time_point;
requires __is_valid_clock_time_point_v<typename _Tp::time_point, _Tp>;

_Tp::is_steady;
requires _IsSame<decltype(_Tp::is_steady), const bool>::value;

_Tp::now();
requires _IsSame<decltype(_Tp::now()), typename _Tp::time_point>::value;
};

template <class _Tp>
struct _LIBCPP_NO_SPECIALIZATIONS is_clock : bool_constant<is_clock_v<_Tp>> {};

} // namespace chrono

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP_STD_VER
#endif // _LIBCPP___CHRONO_IS_CLOCK_H
4 changes: 4 additions & 0 deletions libcxx/include/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ template <class ToDuration, class Rep, class Period>
template <class ToDuration, class Rep, class Period>
constexpr ToDuration round(const duration<Rep, Period>& d); // C++17

template <class T> struct is_clock; // C++20
template <class T> inline constexpr bool is_clock_v = is_clock<T>::value; // C++20

// duration I/O
template<class charT, class traits, class Rep, class Period> // C++20
basic_ostream<charT, traits>&
Expand Down Expand Up @@ -1057,6 +1060,7 @@ constexpr chrono::year operator ""y(unsigned lo
# include <__chrono/day.h>
# include <__chrono/exception.h>
# include <__chrono/hh_mm_ss.h>
# include <__chrono/is_clock.h>
# include <__chrono/literals.h>
# include <__chrono/local_info.h>
# include <__chrono/month.h>
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/module.modulemap.in
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,10 @@ module std [system] {
header "__chrono/high_resolution_clock.h"
export *
}
module is_clock {
header "__chrono/is_clock.h"
export std_core.type_traits.integral_constant
}
module leap_second {
header "__chrono/leap_second.h"
}
Expand Down
4 changes: 2 additions & 2 deletions libcxx/modules/std/chrono.inc
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export namespace std {

using std::chrono::duration_values;

// using std::chrono::is_clock;
// using std::chrono::is_clock_v;
using std::chrono::is_clock;
using std::chrono::is_clock_v;

// [time.duration.nonmember], duration arithmetic
using std::chrono::operator+;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// REQUIRES: std-at-least-c++20

#include <chrono>
#include <ratio>

struct EmptyStruct {};

// Test structs missing required members
struct MissingRep {
using period = std::ratio<1>;
using duration = std::chrono::seconds;
using time_point = std::chrono::time_point<MissingRep>;
static constexpr bool is_steady = false;
static time_point now();
};

struct MissingPeriod {
using rep = long;
using duration = std::chrono::seconds;
using time_point = std::chrono::time_point<MissingPeriod>;
static constexpr bool is_steady = false;
static time_point now();
};

struct MissingDuration {
using rep = long;
using time_point = long;
static constexpr bool is_steady = false;
static time_point now();
};

struct MissingTimePoint {
using rep = long;
using period = std::ratio<1>;
using duration = std::chrono::seconds;
static constexpr bool is_steady = false;
static std::chrono::time_point<MissingTimePoint> now();
};

struct MissingIsSteady {
using rep = long;
using period = std::ratio<1>;
using duration = std::chrono::seconds;
using time_point = std::chrono::time_point<MissingIsSteady>;
static time_point now();
};

struct MissingNow {
using rep = long;
using period = std::ratio<1>;
using duration = std::chrono::seconds;
using time_point = std::chrono::time_point<MissingNow>;
static constexpr bool is_steady = false;
};

// Valid clock types
struct ValidSteadyClock {
using rep = long long;
using period = std::nano;
using duration = std::chrono::nanoseconds;
using time_point = std::chrono::time_point<ValidSteadyClock>;
static constexpr bool is_steady = true;
static time_point now();
};

struct ValidSystemClock {
using rep = long long;
using period = std::micro;
using duration = std::chrono::microseconds;
using time_point = std::chrono::time_point<ValidSystemClock>;
static constexpr bool is_steady = false;
static time_point now();
};

// Test clocks with invalid is_steady type
struct WrongIsSteadyType {
using rep = long;
using period = std::ratio<1>;
using duration = std::chrono::seconds;
using time_point = std::chrono::time_point<WrongIsSteadyType>;
static bool is_steady; // Not const bool
static time_point now();
};

struct WrongIsSteadyNonBool {
using rep = long;
using period = std::ratio<1>;
using duration = std::chrono::seconds;
using time_point = std::chrono::time_point<WrongIsSteadyNonBool>;
static constexpr int is_steady = 1; // Not bool
static time_point now();
};

// Test clocks with invalid now() return type
struct WrongNowReturnType {
using rep = long;
using period = std::ratio<1>;
using duration = std::chrono::seconds;
using time_point = std::chrono::time_point<WrongNowReturnType>;
static constexpr bool is_steady = false;
static int now(); // Wrong return type
};

// Test clocks with invalid period type
struct WrongPeriodType {
using rep = long;
using period = int; // Not a ratio
using duration = std::chrono::seconds;
using time_point = std::chrono::time_point<WrongPeriodType>;
static constexpr bool is_steady = false;
static time_point now();
};

// Test clocks with invalid rep type (neither arithmetic nor numeric_limits specialized)
struct InvalidRepType {
using rep = EmptyStruct; // Not arithmetic, no numeric_limits specialization
using period = std::ratio<1>;
using duration = std::chrono::duration<EmptyStruct>;
using time_point = std::chrono::time_point<InvalidRepType, duration>;
static constexpr bool is_steady = false;
static time_point now();
};

// Test clocks with wrong duration type
struct WrongDurationType {
using rep = long;
using period = std::ratio<1>;
using duration = std::chrono::milliseconds; // Should be duration<long, ratio<1>>
using time_point = std::chrono::time_point<WrongDurationType>;
static constexpr bool is_steady = false;
static time_point now();
};

// Test clocks with wrong time_point type
struct WrongTimePointType {
using rep = long;
using period = std::ratio<1>;
using duration = std::chrono::duration<long, std::ratio<1>>;
using time_point = int; // Not a time_point
static constexpr bool is_steady = false;
static time_point now();
};

struct WrongTimePointClock {
using rep = long;
using period = std::ratio<1>;
using duration = std::chrono::duration<long, std::ratio<1>>;
using time_point = std::chrono::time_point<ValidSystemClock>; // Wrong clock type
static constexpr bool is_steady = false;
static time_point now();
};

// Valid clock with time_point that has matching duration instead of matching clock
struct ValidClockWithDurationMatch {
using rep = int;
using period = std::milli;
using duration = std::chrono::duration<int, std::milli>;
using time_point = std::chrono::time_point<ValidSystemClock, duration>; // Valid: matches duration
static constexpr bool is_steady = false;
static time_point now();
};

int main(int, char**) {
// Test both is_clock and is_clock_v
static_assert(std::chrono::is_clock<std::chrono::system_clock>::value);
static_assert(std::chrono::is_clock_v<std::chrono::system_clock>);

// Test standard clock types
static_assert(std::chrono::is_clock_v<std::chrono::system_clock>);
#ifdef _LIBCPP_HAS_MONOTONIC_CLOCK
static_assert(std::chrono::is_clock_v<std::chrono::steady_clock>);
#endif
static_assert(std::chrono::is_clock_v<std::chrono::high_resolution_clock>);

// Test non-clock types
static_assert(!std::chrono::is_clock_v<EmptyStruct>);
static_assert(!std::chrono::is_clock_v<int>);
static_assert(!std::chrono::is_clock_v<void>);
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock::time_point>);
#ifdef _LIBCPP_HAS_MONOTONIC_CLOCK
static_assert(!std::chrono::is_clock_v<std::chrono::steady_clock::time_point>);
#endif
static_assert(!std::chrono::is_clock_v<std::chrono::seconds>);
static_assert(!std::chrono::is_clock_v<std::chrono::milliseconds>);

// Test structs missing required members
static_assert(!std::chrono::is_clock_v<MissingRep>);
static_assert(!std::chrono::is_clock_v<MissingPeriod>);
static_assert(!std::chrono::is_clock_v<MissingDuration>);
static_assert(!std::chrono::is_clock_v<MissingTimePoint>);
static_assert(!std::chrono::is_clock_v<MissingIsSteady>);
static_assert(!std::chrono::is_clock_v<MissingNow>);

// Test valid custom clocks
static_assert(std::chrono::is_clock_v<ValidSteadyClock>);
static_assert(std::chrono::is_clock_v<ValidSystemClock>);
static_assert(std::chrono::is_clock_v<ValidClockWithDurationMatch>);

// Test clocks with invalid is_steady type
static_assert(!std::chrono::is_clock_v<WrongIsSteadyType>); // is_steady not const bool
static_assert(!std::chrono::is_clock_v<WrongIsSteadyNonBool>); // is_steady not bool type

// Test clocks with invalid now() return type
static_assert(!std::chrono::is_clock_v<WrongNowReturnType>); // now() doesn't return time_point

// Test clocks with invalid period type
static_assert(!std::chrono::is_clock_v<WrongPeriodType>); // period is not a ratio

// Test clocks with invalid rep type
static_assert(!std::chrono::is_clock_v<InvalidRepType>); // rep is not arithmetic and no numeric_limits

// Test clocks with wrong duration type
static_assert(!std::chrono::is_clock_v<WrongDurationType>); // duration doesn't match duration<rep, period>

// Test clocks with wrong time_point type
static_assert(!std::chrono::is_clock_v<WrongTimePointType>); // time_point is not a time_point
static_assert(!std::chrono::is_clock_v<WrongTimePointClock>); // time_point has wrong clock and wrong duration

// cv-qualified and reference types
static_assert(std::chrono::is_clock_v<const std::chrono::system_clock>);
static_assert(std::chrono::is_clock_v<volatile std::chrono::system_clock>);
static_assert(std::chrono::is_clock_v<const volatile std::chrono::system_clock>);
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock&>);
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock&&>);
static_assert(!std::chrono::is_clock_v<const std::chrono::system_clock&>);

// array and pointer types
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock[]>);
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock[10]>);
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock*>);
static_assert(!std::chrono::is_clock_v<std::chrono::system_clock* const>);

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// REQUIRES: std-at-least-c++20

#include <chrono>
#include <ratio>

namespace std::chrono {
// try adding specializations to is_clock
template <>
struct is_clock<int> : std::false_type {}; // expected-error@*:* {{'is_clock' cannot be specialized}}

template <>
constexpr bool is_clock_v<float> = false; // expected-error@*:* {{'is_clock_v' cannot be specialized}}

} // namespace std::chrono
Loading