Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Clock.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4
6
7namespace Stroika::Foundation::Time {
8
9 namespace Private_ {
10 // From https://stackoverflow.com/questions/35282308/convert-between-c11-clocks
11 // EXPECIALLY read commentary...
12 template <typename DstTimePointT, typename SrcTimePointT, typename DstClockT = typename DstTimePointT::clock, typename SrcClockT = typename SrcTimePointT::clock>
13 DstTimePointT clock_cast_0th (const SrcTimePointT tp)
14 {
15 const auto src_now = SrcClockT::now ();
16 const auto dst_now = DstClockT::now ();
17 return dst_now + chrono::duration_cast<typename DstClockT::duration> (tp - src_now);
18 }
19 namespace detail {
20 template <typename DurationT, typename ReprT = typename DurationT::rep>
21 constexpr DurationT max_duration () noexcept
22 {
23 return DurationT{std::numeric_limits<ReprT>::max ()};
24 }
25 template <typename DurationT>
26 constexpr DurationT abs_duration (const DurationT d) noexcept
27 {
28 return DurationT{(d.count () < 0) ? -d.count () : d.count ()};
29 }
30 }
31 template <typename DstTimePointT, typename SrcTimePointT, typename DstDurationT = typename DstTimePointT::duration, typename SrcDurationT = typename SrcTimePointT::duration,
32 typename DstClockT = typename DstTimePointT::clock, typename SrcClockT = typename SrcTimePointT::clock>
33 DstTimePointT clock_cast_2nd (const SrcTimePointT tp,
34 const SrcDurationT tolerance = std::chrono::duration_cast<SrcDurationT> (std::chrono::nanoseconds{100}),
35 const int limit = 4)
36 {
37 Require (limit > 0);
38 auto itercnt = 0;
39 auto src_now = SrcTimePointT{};
40 auto dst_now = DstTimePointT{};
41 auto epsilon = detail::max_duration<SrcDurationT> ();
42 do {
43 const auto src_before = SrcClockT::now ();
44 const auto dst_between = DstClockT::now ();
45 const auto src_after = SrcClockT::now ();
46 const auto src_diff = src_after - src_before;
47 const auto delta = detail::abs_duration (src_diff);
48 if (delta < epsilon) {
49 src_now = src_before + src_diff / 2;
50 dst_now = dst_between;
51 epsilon = std::chrono::duration_cast<SrcDurationT> (delta);
52 }
53 if (++itercnt >= limit)
54 break;
55 } while (epsilon > tolerance);
56 return dst_now + chrono::duration_cast<typename DstClockT::duration> (tp - src_now);
57 }
58#if __cpp_lib_chrono >= 201907L
59 // @todo find better way to check if should use clock_cast or my_private_clock_cast
60 // clang-format off
61 template <typename DESTINATION_CLOCK_T, typename SOURCE_CLOCK_T, typename DURATION_T>
62 constexpr bool kCanUseStdClockCnv_ =
63 same_as<DESTINATION_CLOCK_T,SOURCE_CLOCK_T>
64 or (
65 ( same_as<DESTINATION_CLOCK_T, chrono::system_clock> or same_as<DESTINATION_CLOCK_T, chrono::utc_clock> or same_as<DESTINATION_CLOCK_T, chrono::gps_clock> or same_as<DESTINATION_CLOCK_T, chrono::file_clock> or same_as<DESTINATION_CLOCK_T, chrono::tai_clock>)
66 and (same_as<SOURCE_CLOCK_T, chrono::system_clock> or same_as<SOURCE_CLOCK_T, chrono::utc_clock> or same_as<SOURCE_CLOCK_T, chrono::gps_clock> or same_as<SOURCE_CLOCK_T, chrono::file_clock> or same_as<SOURCE_CLOCK_T, chrono::tai_clock>)
67 )
68 ;
69 // clang-format on
70#else
71 template <typename DESTINATION_CLOCK_T, typename SOURCE_CLOCK_T, typename DURATION_T>
72 constexpr bool kCanUseStdClockCnv_ = false;
73#endif
74 }
75
76 /*
77 ********************************************************************************
78 ****************************** Time::clock_cast ********************************
79 ********************************************************************************
80 */
81 template <typename DESTINATION_CLOCK_T, typename SOURCE_CLOCK_T, typename DURATION_T>
82 inline auto clock_cast (chrono::time_point<SOURCE_CLOCK_T, DURATION_T> tp) -> typename DESTINATION_CLOCK_T::time_point
83 {
84#if __cpp_lib_chrono >= 201907L
85 if constexpr (Private_::kCanUseStdClockCnv_<DESTINATION_CLOCK_T, SOURCE_CLOCK_T, DURATION_T>) {
86 return chrono::clock_cast<DESTINATION_CLOCK_T> (tp);
87 }
88#endif
89 return Private_::clock_cast_2nd<typename DESTINATION_CLOCK_T::time_point> (tp); //return clock_cast_0th<DstTimePointT> (tp);
90 }
91 template <typename DESTINATION_CLOCK_T, template <typename> typename RANGE, typename SOURCE_CLOCK_T, typename DURATION_T>
92 RANGE<typename DESTINATION_CLOCK_T::time_point> clock_cast (RANGE<chrono::time_point<SOURCE_CLOCK_T, DURATION_T>> tpRange)
93 {
94 using RESULT_TIMERANGE = typename DESTINATION_CLOCK_T::time_point;
95 using RESULT_RANGE_TYPE = RANGE<RESULT_TIMERANGE>;
96 if (tpRange.empty ()) {
97 return RESULT_RANGE_TYPE{};
98 }
99 /*
100 * Naive implementation:
101 * return Range<DisplayedRealtimeClock::time_point>{Time::clock_cast<DisplayedRealtimeClock> (tpRange.GetLowerBound ()),
102 * Time::clock_cast<DisplayedRealtimeClock> (tpRange.GetUpperBound ())};
103 * wrong because jitter in estimate/conversion and can result in negative Range/assert error. Leverage fact that conversion
104 * of one is just an offset, and use same offset for the second one.
105 *
106 * But not all clock conversions have this jitter issue.
107 */
108 if constexpr (Private_::kCanUseStdClockCnv_<DESTINATION_CLOCK_T, SOURCE_CLOCK_T, DURATION_T>) {
109 return RESULT_RANGE_TYPE{clock_cast<DESTINATION_CLOCK_T> (tpRange.GetLowerBound ()),
110 Time::clock_cast<DESTINATION_CLOCK_T> (tpRange.GetUpperBound ())};
111 }
112 else {
113 typename DESTINATION_CLOCK_T::time_point lb = clock_cast<DESTINATION_CLOCK_T> (tpRange.GetLowerBound ());
114 auto diff = lb.time_since_epoch () - tpRange.GetLowerBound ().time_since_epoch ();
115 return RESULT_RANGE_TYPE{lb, RESULT_TIMERANGE{tpRange.GetUpperBound ().time_since_epoch () + diff}};
116 }
117 }
118
119 /*
120 ********************************************************************************
121 **************** AppStartZeroedClock<BASE_CLOCK_T, DURATION_T> *****************
122 ********************************************************************************
123 */
124 template <typename BASE_CLOCK_T, typename DURATION_T>
125 [[nodiscard]] inline auto AppStartZeroedClock<BASE_CLOCK_T, DURATION_T>::now () noexcept -> time_point
126 {
127 return time_point{Implementation_::now ().time_since_epoch ()} - kTimeAppStartedOffset_;
128 }
129
130}