Skip to content

Commit

Permalink
<chrono>: Avoid unnecessary use of concepts (#1787)
Browse files Browse the repository at this point in the history
  • Loading branch information
StephanTLavavej authored Mar 29, 2021
1 parent 3017b91 commit dde4623
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 91 deletions.
221 changes: 134 additions & 87 deletions stl/inc/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ namespace chrono {
using rep = typename _Duration::rep;
using period = typename _Duration::period;

static_assert(_Is_duration_v<_Duration>, "duration must be an instance of std::duration");
static_assert(_Is_duration_v<_Duration>,
"N4885 [time.point.general]/1 mandates Duration to be a specialization of chrono::duration.");

constexpr time_point() = default;

Expand Down Expand Up @@ -2128,8 +2129,11 @@ namespace chrono {
}

template <class _Duration>
requires _Is_duration_v<_Duration> class hh_mm_ss {
class hh_mm_ss {
public:
static_assert(_Is_duration_v<_Duration>,
"N4885 [time.hms.overview]/2 mandates Duration to be a specialization of chrono::duration.");

static constexpr unsigned int fractional_width = [] {
auto _Num = _Duration::period::num;
constexpr auto _Den = _Duration::period::den;
Expand Down Expand Up @@ -2907,19 +2911,19 @@ namespace chrono {
class zoned_time {
private:
static_assert(_Is_duration_v<_Duration>,
"N4878 [time.zone.zonedtime.overview]/2 requires Duration to be a specialization of chrono::duration.");
"N4885 [time.zone.zonedtime.overview]/2 mandates Duration to be a specialization of chrono::duration.");

using _Traits = zoned_traits<_TimeZonePtr>;

public:
using duration = common_type_t<_Duration, seconds>;

template <class _Traits2 = _Traits, class = decltype(_Traits2::default_zone())>
template <class _Traits2 = _Traits, class = void_t<decltype(_Traits2::default_zone())>>
zoned_time() : _Zone{_Traits::default_zone()} {}
zoned_time(const zoned_time&) = default;
zoned_time& operator=(const zoned_time&) = default;

template <class _Traits2 = _Traits, class = decltype(_Traits2::default_zone())>
template <class _Traits2 = _Traits, class = void_t<decltype(_Traits2::default_zone())>>
zoned_time(const sys_time<_Duration>& _Sys) : _Zone{_Traits::default_zone()}, _Tp{_Sys} {}

explicit zoned_time(_TimeZonePtr _Tz) noexcept /* strengthened */ : _Zone{_STD move(_Tz)} {}
Expand Down Expand Up @@ -3405,131 +3409,174 @@ namespace chrono {

// [time.clock.cast.sys]

// TRANSITION, GH-395 workaround, is_same_v -> same_as
template <class _Ty, class _Clock>
concept _Is_time_point = requires {
typename _Ty::duration;
requires is_same_v<time_point<_Clock, typename _Ty::duration>, _Ty>;
};
template <class _TimePoint, class _Clock>
inline constexpr bool _Is_time_point_for_clock = false;

template <class _Clock, class _Duration>
concept _Convertible_to_sys_time =
is_same_v<_Clock, system_clock> || requires(const time_point<_Clock, _Duration>& _Time) {
{ _Clock::to_sys(_Time) }
->_Is_time_point<system_clock>;
};

template <class _Clock, class _Duration>
concept _Convertible_from_sys_time = is_same_v<_Clock, system_clock> || requires(const sys_time<_Duration>& _Time) {
{ _Clock::from_sys(_Time) }
->_Is_time_point<_Clock>;
};
inline constexpr bool _Is_time_point_for_clock<time_point<_Clock, _Duration>, _Clock> = true;

template <class _SourceClock>
struct clock_time_conversion<system_clock, _SourceClock> {
template <class _Duration>
template <class _Duration, class _SourceClock2 = _SourceClock,
class =
void_t<decltype(_SourceClock2::to_sys(_STD declval<const time_point<_SourceClock2, _Duration>&>()))>>
_NODISCARD auto operator()(const time_point<_SourceClock, _Duration>& _Time) const
noexcept(noexcept(_SourceClock::to_sys(_Time))) /* strengthened */
requires _Convertible_to_sys_time<_SourceClock, _Duration> {
noexcept(noexcept(_SourceClock::to_sys(_Time))) /* strengthened */ {
static_assert(_Is_time_point_for_clock<decltype(_SourceClock::to_sys(_Time)), system_clock>,
"N4885 [time.clock.cast.sys]/2: Mandates: SourceClock::to_sys(t) returns a sys_time<Duration>");
return _SourceClock::to_sys(_Time);
}
};

template <class _DestClock>
struct clock_time_conversion<_DestClock, system_clock> {
template <class _Duration>
template <class _Duration, class _DestClock2 = _DestClock,
class = void_t<decltype(_DestClock2::from_sys(_STD declval<const sys_time<_Duration>&>()))>>
_NODISCARD auto operator()(const sys_time<_Duration>& _Time) const
noexcept(noexcept(_DestClock::from_sys(_Time))) /* strengthened */
requires _Convertible_from_sys_time<_DestClock, _Duration> {
noexcept(noexcept(_DestClock::from_sys(_Time))) /* strengthened */ {
static_assert(_Is_time_point_for_clock<decltype(_DestClock::from_sys(_Time)), _DestClock>,
"N4885 [time.clock.cast.sys]/5: Mandates: DestClock::from_sys(t) returns a "
"time_point<DestClock, Duration>");
return _DestClock::from_sys(_Time);
}
};

// [time.clock.cast.utc]

template <class _Clock, class _Duration>
concept _Convertible_to_utc_time =
is_same_v<_Clock,
utc_clock> || is_same_v<_Clock, system_clock> || requires(const time_point<_Clock, _Duration>& _Time) {
{ _Clock::to_utc(_Time) }
->_Is_time_point<utc_clock>;
};

template <class _Clock, class _Duration>
concept _Convertible_from_utc_time =
is_same_v<_Clock, utc_clock> || is_same_v<_Clock, system_clock> || requires(const utc_time<_Duration>& _Time) {
{ _Clock::from_utc(_Time) }
->_Is_time_point<_Clock>;
};

template <class _SourceClock>
struct clock_time_conversion<utc_clock, _SourceClock> {
template <class _Duration>
template <class _Duration, class _SourceClock2 = _SourceClock,
class =
void_t<decltype(_SourceClock2::to_utc(_STD declval<const time_point<_SourceClock2, _Duration>&>()))>>
_NODISCARD auto operator()(const time_point<_SourceClock, _Duration>& _Time) const
noexcept(noexcept(_SourceClock::to_utc(_Time))) /* strengthened */
requires _Convertible_to_utc_time<_SourceClock, _Duration> {
noexcept(noexcept(_SourceClock::to_utc(_Time))) /* strengthened */ {
static_assert(_Is_time_point_for_clock<decltype(_SourceClock::to_utc(_Time)), utc_clock>,
"N4885 [time.clock.cast.utc]/2: Mandates: SourceClock::to_utc(t) returns a utc_time<Duration>");
return _SourceClock::to_utc(_Time);
}
};

template <class _DestClock>
struct clock_time_conversion<_DestClock, utc_clock> {
template <class _Duration>
template <class _Duration, class _DestClock2 = _DestClock,
class = void_t<decltype(_DestClock2::from_utc(_STD declval<const utc_time<_Duration>&>()))>>
_NODISCARD auto operator()(const utc_time<_Duration>& _Time) const
noexcept(noexcept(_DestClock::from_utc(_Time))) /* strengthened */
requires _Convertible_from_utc_time<_DestClock, _Duration> {
noexcept(noexcept(_DestClock::from_utc(_Time))) /* strengthened */ {
static_assert(_Is_time_point_for_clock<decltype(_DestClock::from_utc(_Time)), _DestClock>,
"N4885 [time.clock.cast.utc]/5: Mandates: DestClock::from_utc(t) returns a "
"time_point<DestClock, Duration>");
return _DestClock::from_utc(_Time);
}
};

// [time.clock.cast.fn]

// FUNCTION TEMPLATE clock_cast
enum class _Clock_cast_strategy {
_Direct,
_Via_sys,
_Via_utc,
_Via_utc_from_sys,
_Via_sys_from_utc,
_Two_step_ambiguous,
_Three_step_ambiguous,
_None,
};

template <class _Conv1, class _Conv2, class _Tp, class = void>
inline constexpr bool _Has_two_step_conversion = false;

template <class _Conv1, class _Conv2, class _Tp>
inline constexpr bool
_Has_two_step_conversion<_Conv1, _Conv2, _Tp, void_t<decltype(_Conv1{}(_Conv2{}(_STD declval<_Tp>())))>> = true;

template <class _Conv1, class _Conv2, class _Conv3, class _Tp, class = void>
inline constexpr bool _Has_three_step_conversion = false;

template <class _Conv1, class _Conv2, class _Conv3, class _Tp>
inline constexpr bool _Has_three_step_conversion<_Conv1, _Conv2, _Conv3, _Tp,
void_t<decltype(_Conv1{}(_Conv2{}(_Conv3{}(_STD declval<_Tp>()))))>> = true;

template <class _DestClock, class _SourceClock, class _Duration>
_NODISCARD _CONSTEVAL _Clock_cast_strategy _Choose_clock_cast() noexcept {
using _Tp = const time_point<_SourceClock, _Duration>&;

if constexpr (is_invocable_v<clock_time_conversion<_DestClock, _SourceClock>, _Tp>) {
return _Clock_cast_strategy::_Direct;
} else {
constexpr bool _Has_sys = _Has_two_step_conversion< //
clock_time_conversion<_DestClock, system_clock>, //
clock_time_conversion<system_clock, _SourceClock>, _Tp>;

constexpr bool _Has_utc = _Has_two_step_conversion< //
clock_time_conversion<_DestClock, utc_clock>, //
clock_time_conversion<utc_clock, _SourceClock>, _Tp>;

if constexpr (_Has_sys && _Has_utc) {
return _Clock_cast_strategy::_Two_step_ambiguous;
} else if constexpr (_Has_sys) {
return _Clock_cast_strategy::_Via_sys;
} else if constexpr (_Has_utc) {
return _Clock_cast_strategy::_Via_utc;
} else {
constexpr bool _Has_utc_from_sys = _Has_three_step_conversion< //
clock_time_conversion<_DestClock, utc_clock>, //
clock_time_conversion<utc_clock, system_clock>, //
clock_time_conversion<system_clock, _SourceClock>, _Tp>;

constexpr bool _Has_sys_from_utc = _Has_three_step_conversion< //
clock_time_conversion<_DestClock, system_clock>, //
clock_time_conversion<system_clock, utc_clock>, //
clock_time_conversion<utc_clock, _SourceClock>, _Tp>;

if constexpr (_Has_utc_from_sys && _Has_sys_from_utc) {
return _Clock_cast_strategy::_Three_step_ambiguous;
} else if constexpr (_Has_utc_from_sys) {
return _Clock_cast_strategy::_Via_utc_from_sys;
} else if constexpr (_Has_sys_from_utc) {
return _Clock_cast_strategy::_Via_sys_from_utc;
} else {
return _Clock_cast_strategy::_None;
}
}
}
}

template <class _DestClock, class _SourceClock, class _Duration>
inline constexpr auto _Clock_cast_choice = _Choose_clock_cast<_DestClock, _SourceClock, _Duration>();

// FUNCTION TEMPLATE clock_cast
template <class _DestClock, class _SourceClock, class _Duration,
enable_if_t<_Clock_cast_choice<_DestClock, _SourceClock, _Duration> != _Clock_cast_strategy::_None, int> = 0>
_NODISCARD auto clock_cast(const time_point<_SourceClock, _Duration>& _Time) {
constexpr bool _Has_direct_conversion =
is_invocable_v<clock_time_conversion<_DestClock, _SourceClock>, decltype(_Time)>;

constexpr bool _Utc_from_src = _Convertible_to_utc_time<_SourceClock, _Duration>;
constexpr bool _Sys_from_src = _Convertible_to_sys_time<_SourceClock, _Duration>;
constexpr bool _Dest_from_utc = _Convertible_from_utc_time<_DestClock, _Duration>;
constexpr bool _Dest_from_sys = _Convertible_from_sys_time<_DestClock, _Duration>;

constexpr bool _Has_utc_conversion = _Dest_from_utc && _Utc_from_src;
constexpr bool _Has_sys_conversion = _Dest_from_sys && _Sys_from_src;
static_assert(_Has_direct_conversion || !(_Has_utc_conversion && _Has_sys_conversion),
"A two-step clock time conversion is required to be unique, either through utc_clock or system_clock, but "
"not both (N4878 [time.clock.cast.fn]/2.)");

constexpr bool _Has_sys_utc_conversion = _Dest_from_sys && _Utc_from_src;
constexpr bool _Has_utc_sys_conversion = _Dest_from_utc && _Sys_from_src;
static_assert(_Has_direct_conversion || _Has_utc_conversion || _Has_sys_conversion
|| !(_Has_utc_sys_conversion && _Has_sys_utc_conversion),
"A three-step clock time conversion is required to be unique, either utc-to-system or system-to-utc, but "
"not both (N4878 [time.clock.cast.fn]/2).");
constexpr auto _Strat = _Clock_cast_choice<_DestClock, _SourceClock, _Duration>;

// clang-format off
if constexpr (_Has_direct_conversion) {
if constexpr (_Strat == _Clock_cast_strategy::_Direct) {
return clock_time_conversion<_DestClock, _SourceClock>{}(_Time);
} else if constexpr (_Has_utc_conversion) {
return clock_time_conversion<_DestClock, utc_clock>{}(
clock_time_conversion<utc_clock, _SourceClock>{}(_Time));
} else if constexpr (_Has_sys_conversion) {
} else if constexpr (_Strat == _Clock_cast_strategy::_Via_sys) {
return clock_time_conversion<_DestClock, system_clock>{}(
clock_time_conversion<system_clock, _SourceClock>{}(_Time));
} else if constexpr (_Has_sys_utc_conversion) {
return clock_time_conversion<_DestClock, system_clock>{}(
clock_time_conversion<system_clock, utc_clock>{}(
clock_time_conversion<utc_clock, _SourceClock>{}(_Time)));
} else if constexpr (_Has_utc_sys_conversion) {
clock_time_conversion<system_clock, _SourceClock>{}(_Time));
} else if constexpr (_Strat == _Clock_cast_strategy::_Via_utc) {
return clock_time_conversion<_DestClock, utc_clock>{}(
clock_time_conversion<utc_clock, system_clock>{}(
clock_time_conversion<system_clock, _SourceClock>{}(_Time)));
clock_time_conversion<utc_clock, _SourceClock>{}(_Time));
} else if constexpr (_Strat == _Clock_cast_strategy::_Via_utc_from_sys) {
return clock_time_conversion<_DestClock, utc_clock>{}( //
clock_time_conversion<utc_clock, system_clock>{}(
clock_time_conversion<system_clock, _SourceClock>{}(_Time)));
} else if constexpr (_Strat == _Clock_cast_strategy::_Via_sys_from_utc) {
return clock_time_conversion<_DestClock, system_clock>{}( //
clock_time_conversion<system_clock, utc_clock>{}(
clock_time_conversion<utc_clock, _SourceClock>{}(_Time)));
} else if constexpr (_Strat == _Clock_cast_strategy::_Two_step_ambiguous) {
static_assert(_Always_false<_Duration>,
"A two-step clock time conversion is required to be unique, "
"either through utc_clock or system_clock, but not both (N4878 [time.clock.cast.fn]/2).");
} else if constexpr (_Strat == _Clock_cast_strategy::_Three_step_ambiguous) {
static_assert(_Always_false<_Duration>,
"A three-step clock time conversion is required to be unique, "
"either utc-to-system or system-to-utc, but not both (N4878 [time.clock.cast.fn]/2).");
} else {
static_assert(_Always_false<_Duration>, "No clock time conversion exists from source clock type to "
"destination clock type (N4878 [time.clock.cast.fn]/1).");
static_assert(_Always_false<_Duration>, "should be unreachable");
}
// clang-format on
}

// [time.parse]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,13 @@ void timezone_names_test() {
}

// See GH-1786. These may change over time and might have to be removed from this test.

// these are some examples in which the ICU.dll and IANA database diverge in what they consider a zone or a link
// These are some examples in which the ICU.dll and IANA database diverge in what they consider a zone or a link.
assert(_Locate_zone_impl(my_tzdb.links, "Atlantic/Faroe") != nullptr); // is a time_zone in IANA
assert(_Locate_zone_impl(my_tzdb.zones, "Africa/Addis_Ababa") != nullptr); // is a time_zone_link in IANA
assert(_Locate_zone_impl(my_tzdb.links, "PST") != nullptr); // time_zone_link does not exist in IANA
assert(_Locate_zone_impl(my_tzdb.links, "Africa/Asmara") != nullptr); // matches IANA but target is wrong
assert(_Locate_zone_impl(my_tzdb.links, "Africa/Asmara") != nullptr); // matches IANA but target is different
assert(_Locate_zone_impl(my_tzdb.links, "Africa/Asmara")->target() == "Africa/Asmera"); // target == Africa/Nairobi
assert(_Locate_zone_impl(my_tzdb.zones, "America/Nuuk") == nullptr); // does not exist in ICU (very rare)
assert(_Locate_zone_impl(my_tzdb.zones, "America/Nuuk") == nullptr); // added in ICU 68, update test when it arrives
}

void validate_timezone_transitions(const time_zone* tz, const Transition& transition) {
Expand Down

0 comments on commit dde4623

Please sign in to comment.