Zth (libzth)
time.h
Go to the documentation of this file.
1 #ifndef ZTH_TIME_H
2 #define ZTH_TIME_H
3 /*
4  * Zth (libzth), a cooperative userspace multitasking library.
5  * Copyright (C) 2019-2022 Jochem Rutgers
6  *
7  * This Source Code Form is subject to the terms of the Mozilla Public
8  * License, v. 2.0. If a copy of the MPL was not distributed with this
9  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
10  */
11 
17 #ifdef __cplusplus
18 
19 # include <libzth/allocator.h>
20 # include <libzth/config.h>
21 # include <libzth/util.h>
22 
23 # include <algorithm>
24 # include <cmath>
25 # include <cstdio>
26 # include <ctime>
27 # include <inttypes.h>
28 # include <limits>
29 
30 # if defined(ZTH_OS_MAC) || defined(ZTH_OS_BAREMETAL)
31 # ifdef ZTH_CUSTOM_CLOCK_GETTIME
32 extern "C" int clock_gettime(int clk_id, struct timespec* res);
33 # endif
34 extern "C" int
35 clock_nanosleep(int clk_id, int flags, struct timespec const* request, struct timespec* remain);
36 # ifndef CLOCK_MONOTONIC
37 # define CLOCK_MONOTONIC 1
38 # endif
39 # ifndef TIMER_ABSTIME
40 # define TIMER_ABSTIME 1
41 # endif
42 # endif
43 
44 namespace zth {
45 
46 class Timestamp;
47 
52 class TimeInterval {
54 private:
55  // Do not convert automatically.
56  TimeInterval(Timestamp const&);
57 
58 public:
59  static long const BILLION = 1000000000L;
60 
61  constexpr static TimeInterval infinity() noexcept
62  {
63  return TimeInterval(std::numeric_limits<time_t>::max());
64  }
65 
66  constexpr static TimeInterval null() noexcept
67  {
68  return TimeInterval();
69  }
70 
71  constexpr TimeInterval() noexcept
72  : m_t()
73  , m_negative()
74  {}
75 
76 # if __cplusplus >= 201103L
77  // cppcheck-suppress noExplicitConstructor
78  constexpr TimeInterval(time_t s, long ns = 0, bool negative = false) noexcept
79  : m_t{s, ns}
80  , m_negative{negative}
81  {}
82 # else
83  // cppcheck-suppress noExplicitConstructor
84  TimeInterval(time_t s, long ns = 0, bool negative = false) noexcept
85  : m_t()
86  , m_negative(negative)
87  {
88  m_t.tv_sec = s;
89  m_t.tv_nsec = ns;
91  }
92 # endif
93 
94  // cppcheck-suppress noExplicitConstructor
95  constexpr TimeInterval(struct timespec const& ts) noexcept
96  : m_t(ts)
97  , m_negative()
98  {}
99 
100  TimeInterval& operator=(TimeInterval const& t) noexcept
101  {
102  m_t = t.m_t;
103  m_negative = t.m_negative;
104  return *this;
105  }
106 
107  constexpr TimeInterval(TimeInterval const& t) noexcept
108  : m_t(t.ts())
109  , m_negative(t.isNegative())
110  {}
111 
112  // cppcheck-suppress noExplicitConstructor
113  TimeInterval(float dt)
114  : m_t()
115  , m_negative()
116  {
117  init_float<float>(dt);
118  }
119 
120  // cppcheck-suppress noExplicitConstructor
121  TimeInterval(double dt)
122  : m_t()
123  , m_negative()
124  {
125  init_float<double>(dt);
126  }
127 
128  // cppcheck-suppress noExplicitConstructor
129  TimeInterval(long double dt)
130  : m_t()
131  , m_negative()
132  {
133  init_float<long double>(dt);
134  }
135 
136  template <typename T>
137  // cppcheck-suppress noExplicitConstructor
138  constexpr14 TimeInterval(T dt) noexcept
139  : m_t()
140  , m_negative()
141  {
142  init_int<T>(dt);
143  }
144 
145  template <typename T>
147  {
148  if(s > std::numeric_limits<time_t>::max())
149  return TimeInterval(std::numeric_limits<time_t>::max(), 999999999L);
150 
151  return TimeInterval(s);
152  }
153 
154  template <typename T>
156  {
157  T s_ = ms / (T)1000;
158  if((unsigned long long)s_ > (unsigned long long)std::numeric_limits<time_t>::max())
159  return TimeInterval(std::numeric_limits<time_t>::max(), 999999999L);
160 
161  return TimeInterval((time_t)s_, ((long)ms % 1000L) * 1000000L);
162  }
163 
164  template <typename T>
166  {
167  T s_ = us / (T)1000000;
168  if((unsigned long long)s_ > (unsigned long long)std::numeric_limits<time_t>::max())
169  return TimeInterval(std::numeric_limits<time_t>::max(), 999999999L);
170 
171  return TimeInterval((time_t)s_, ((long)us % 1000000L) * 1000L);
172  }
173 
174  template <typename T>
176  {
177  T s_ = ns / (T)1000000000L;
178  if(s_ > std::numeric_limits<time_t>::max())
179  return TimeInterval(std::numeric_limits<time_t>::max(), 999999999L);
180 
181  return TimeInterval((time_t)s_, (long)ns % 1000000000L);
182  }
183 
184 private:
185  template <typename T>
186  void init_float(T dt)
187  {
188  zth_assert(dt == dt); // Should not be NaN
189 
190  m_negative = dt < 0;
191 
192  if(std::fabs(dt) > (T)(std::numeric_limits<time_t>::max() / 2 - 1))
193  m_t.tv_sec = std::numeric_limits<time_t>::max();
194  else
195  m_t.tv_sec = (time_t)std::fabs(dt);
196  m_t.tv_nsec = (long)(std::fmod(std::fabs(dt), (T)1.0) * (T)1e9);
197 
198  if(m_t.tv_nsec >= BILLION) {
199  m_t.tv_nsec -= BILLION;
200  m_t.tv_sec++;
201  }
202  }
203 
204  template <typename T>
205  constexpr14 void init_int(T dt) noexcept
206  {
207  // seconds only.
208  m_negative = dt < 0;
209  m_t.tv_sec = (time_t)(dt >= 0 ? dt : -dt);
210  m_t.tv_nsec = 0;
211  }
212 
213 public:
214  constexpr bool isNormal() const noexcept
215  {
216  return m_t.tv_sec >= 0 && m_t.tv_nsec >= 0 && m_t.tv_nsec < BILLION;
217  }
218 
219  constexpr bool isNegative() const noexcept
220  {
221  return m_negative;
222  }
223 
224  constexpr bool isPositive() const noexcept
225  {
226  return !isNegative();
227  }
228 
229  constexpr bool isNull() const noexcept
230  {
231  return m_t.tv_sec == 0 && m_t.tv_nsec == 0;
232  }
233 
234  constexpr bool isInfinite() const noexcept
235  {
236  return m_t.tv_sec > std::numeric_limits<time_t>::max() / 2;
237  }
238 
239  constexpr bool hasPassed() const noexcept
240  {
241  return isNegative() || isNull();
242  }
243 
244  constexpr struct timespec const& ts() const noexcept
245  {
246  return m_t;
247  }
248 
249  constexpr14 double s() const noexcept
250  {
251  return s<double>();
252  }
253 
254  template <typename T>
255  constexpr14 T s() const noexcept
256  {
257  T t = (T)m_t.tv_sec + (T)m_t.tv_nsec * (T)1e-9;
258  if(m_negative)
259  t = -t;
260  return t;
261  }
262 
263  constexpr bool isAbsBiggerThan(TimeInterval const& t) const noexcept
264  {
265  return m_t.tv_sec > t.m_t.tv_sec
266  || (m_t.tv_sec == t.m_t.tv_sec && m_t.tv_nsec > t.m_t.tv_nsec);
267  }
268 
269  constexpr bool isBiggerThan(TimeInterval const& t) const noexcept
270  {
271  return (!m_negative && t.m_negative)
272  || (!m_negative && !t.m_negative && this->isAbsBiggerThan(t))
273  || (m_negative && t.m_negative && t.isAbsBiggerThan(*this));
274  }
275 
276  constexpr bool operator==(TimeInterval const& rhs) const noexcept
277  {
278  return m_t.tv_nsec == rhs.m_t.tv_nsec && m_t.tv_sec == rhs.m_t.tv_sec
279  && m_negative == rhs.m_negative;
280  }
281 
282  constexpr bool operator>(TimeInterval const& rhs) const noexcept
283  {
284  return this->isBiggerThan(rhs);
285  }
286 
287  constexpr bool operator>=(TimeInterval const& rhs) const noexcept
288  {
289  return *this == rhs || this->isBiggerThan(rhs);
290  }
291 
292  constexpr bool operator<(TimeInterval const& rhs) const noexcept
293  {
294  return !(*this >= rhs);
295  }
296 
297  constexpr bool operator<=(TimeInterval const& rhs) const noexcept
298  {
299  return *this == rhs || !(*this > rhs);
300  }
301 
302  constexpr14 void add(TimeInterval const& t) noexcept
303  {
304  if(t.m_negative == m_negative) {
305  // Check for overflow.
306  if(isInfinite() || t.isInfinite()
307  || std::numeric_limits<time_t>::max() - m_t.tv_sec - 1 <= t.m_t.tv_sec) {
308  m_t.tv_sec = std::numeric_limits<time_t>::max(); // infinite
309  } else {
310  // Add in same sign direction.
311  m_t.tv_sec += t.m_t.tv_sec;
312  m_t.tv_nsec += t.m_t.tv_nsec;
313  if(m_t.tv_nsec > BILLION) {
314  m_t.tv_nsec -= BILLION;
315  m_t.tv_sec++;
316  }
317  }
318  } else {
319  if(t.isAbsBiggerThan(*this)) {
320  // Add in other sign direction with sign flip.
321  m_t.tv_sec = t.m_t.tv_sec - m_t.tv_sec;
322  m_t.tv_nsec = t.m_t.tv_nsec - m_t.tv_nsec;
323  m_negative = !m_negative;
324  } else {
325  // Add in other sign direction (but without sign flip).
326  m_t.tv_sec -= t.m_t.tv_sec;
327  m_t.tv_nsec -= t.m_t.tv_nsec;
328  }
329  if(m_t.tv_nsec < 0) {
330  m_t.tv_nsec += BILLION;
331  m_t.tv_sec--;
332  }
333  }
334  }
335 
336  constexpr14 void sub(TimeInterval const& t) noexcept
337  {
338  add(TimeInterval(t.ts().tv_sec, t.ts().tv_nsec, !t.isNegative()));
339  }
340 
341  template <typename T>
342  constexpr14 void mul(T x) noexcept
343  {
344  *this = TimeInterval(s<T>() * x);
345  }
346 
348  {
349  add(rhs);
350  return *this;
351  }
352 
353  constexpr14 TimeInterval operator+(TimeInterval const& rhs) const noexcept
354  {
355  TimeInterval ti(*this);
356  ti += rhs;
357  return ti;
358  }
359 
361  {
362  sub(rhs);
363  return *this;
364  }
365 
366  constexpr14 TimeInterval operator-(TimeInterval const& rhs) const noexcept
367  {
368  TimeInterval ti(*this);
369  ti -= rhs;
370  return ti;
371  }
372 
374  {
375  return TimeInterval(ts().tv_sec, ts().tv_nsec, !isNegative());
376  }
377 
378  template <typename T>
380  {
381  mul<T>(x);
382  return *this;
383  }
384 
385  template <typename T>
386  constexpr14 TimeInterval operator*(T x) const noexcept
387  {
388  return TimeInterval(s<T>() * x);
389  }
390 
391  template <typename T>
393  {
394  mul<T>((T)1 / x);
395  return *this;
396  }
397 
398  template <typename T>
399  constexpr14 TimeInterval operator/(T x) const noexcept
400  {
401  return TimeInterval(s<T>() / x);
402  }
403 
404  string str() const
405  {
406  string res;
407  if(isInfinite()) {
408  res = "infinity";
409  return res;
410  }
411 
412  if(m_negative)
413  res = "-";
414 
415 # ifdef ZTH_OS_BAREMETAL
416  // Do a simplified print without float formatting support.
417  if(m_t.tv_sec >= 60)
418  res += format("%u s", (unsigned int)m_t.tv_sec);
419  else if(m_t.tv_sec > 0)
420  res +=
421  format("%u.%03u s", (unsigned int)m_t.tv_sec,
422  (unsigned int)(m_t.tv_nsec / 1000000L));
423  else
424  res += format("%u us", (unsigned int)m_t.tv_nsec / 1000U);
425 # else
426  uint64_t d = (uint64_t)(m_t.tv_sec / 3600 / 24);
427  time_t rest = m_t.tv_sec - d * 3600 * 24;
428  bool doPrint = d > 0;
429  if(doPrint)
430  res += format("%" PRIu64 "d:", d);
431 
432  int h = int(rest / 3600);
433  rest -= h * 3600;
434  if(doPrint) {
435  res += format("%02d:", h);
436  } else if(h > 0) {
437  res += format("%d:", h);
438  doPrint = true;
439  }
440 
441  int m = int(rest / 60);
442  rest -= m * 60;
443  if(doPrint) {
444  res += format("%02d:", m);
445  } else if(m > 0) {
446  res += format("%d:", m);
447  doPrint = true;
448  }
449 
450  double sec = (double)rest + (double)m_t.tv_nsec * 1e-9;
451  if(doPrint) {
452  res += format("%06.3f", sec);
453  } else {
454  res += format("%g s", sec);
455  }
456 # endif
457 
458  return res;
459  }
460 
461 private:
462  struct timespec m_t;
463  bool m_negative;
464 };
465 
466 template <>
468 {
469  return value.str();
470 }
471 
472 # if __cplusplus >= 201103L
480 ZTH_EXPORT constexpr14 inline TimeInterval operator"" _s(unsigned long long int x) noexcept
481 {
482  return TimeInterval((time_t)std::min<unsigned long long int>(
483  x, (unsigned long long int)std::numeric_limits<time_t>::max()));
484 }
485 
493 ZTH_EXPORT constexpr14 inline TimeInterval operator"" _ms(unsigned long long int x) noexcept
494 {
495  return TimeInterval::from_ms(x);
496 }
497 
505 ZTH_EXPORT constexpr14 inline TimeInterval operator"" _us(unsigned long long int x) noexcept
506 {
507  return TimeInterval::from_us(x);
508 }
509 
517 ZTH_EXPORT inline TimeInterval operator"" _s(long double x)
518 {
519  return TimeInterval(x);
520 }
521 # endif // C++11
522 
527 class Timestamp {
529 public:
530  // null
531  constexpr Timestamp() noexcept
532  : m_t()
533  {}
534 
535  // cppcheck-suppress noExplicitConstructor
536  constexpr Timestamp(struct timespec const& ts) noexcept
537  : m_t(ts)
538  {}
539 
540  constexpr14 explicit Timestamp(time_t sec, long nsec = 0) noexcept
541  : m_t()
542  {
543  m_t.tv_sec = sec;
544  m_t.tv_nsec = nsec;
545  }
546 
547  // cppcheck-suppress noExplicitConstructor
549  : m_t()
550  {
551  *this = now() + ti;
552  }
553 
554  static Timestamp now()
555  {
556  Timestamp t;
557  int res __attribute__((unused)) = clock_gettime(CLOCK_MONOTONIC, &t.m_t);
558  zth_assert(res == 0);
559  zth_assert(!t.isNull());
560  return t;
561  }
562 
563  constexpr struct timespec const& ts() const noexcept
564  {
565  return m_t;
566  }
567  constexpr operator struct timespec const &() const noexcept
568  {
569  return ts();
570  }
571 
572  constexpr bool isBefore(Timestamp const& t) const noexcept
573  {
574  return ts().tv_sec < t.ts().tv_sec
575  || (ts().tv_sec == t.ts().tv_sec && ts().tv_nsec < t.ts().tv_nsec);
576  }
577 
578  constexpr bool isAfter(Timestamp const& t) const noexcept
579  {
580  return t.isBefore(*this);
581  }
582 
583  bool hasPassed() const noexcept
584  {
585  return now().isBefore(*this);
586  }
587 
588  constexpr bool operator==(Timestamp const& rhs) const noexcept
589  {
590  return ts().tv_nsec == rhs.ts().tv_nsec && ts().tv_sec == rhs.ts().tv_sec;
591  }
592 
593  constexpr bool operator!=(Timestamp const& rhs) const noexcept
594  {
595  return !(*this == rhs);
596  }
597  constexpr bool operator<(Timestamp const& rhs) const noexcept
598  {
599  return this->isBefore(rhs);
600  }
601  constexpr bool operator<=(Timestamp const& rhs) const noexcept
602  {
603  return *this == rhs || this->isBefore(rhs);
604  }
605  constexpr bool operator>(Timestamp const& rhs) const noexcept
606  {
607  return rhs.isBefore(*this);
608  }
609  constexpr bool operator>=(Timestamp const& rhs) const noexcept
610  {
611  return *this == rhs || rhs.isBefore(*this);
612  }
613 
615  {
616  return TimeInterval(t.ts().tv_sec, t.ts().tv_nsec)
617  - TimeInterval(ts().tv_sec, ts().tv_nsec);
618  }
619 
621  {
622  // Assume we are in the past. If so, interval calculations are
623  // simpler than in the general timeTo() case.
624  Timestamp t = now();
625  zth_assert(!isAfter(t));
626 
627  t.m_t.tv_sec -= m_t.tv_sec;
628  t.m_t.tv_nsec -= m_t.tv_nsec;
629  if(t.m_t.tv_nsec < 0) {
630  t.m_t.tv_sec--;
631  t.m_t.tv_nsec += TimeInterval::BILLION;
632  }
633 
634  return TimeInterval(t.m_t.tv_sec, t.m_t.tv_nsec);
635  }
636 
637  constexpr14 void add(TimeInterval const& dt) noexcept
638  {
639  TimeInterval t(m_t.tv_sec, m_t.tv_nsec);
640  t += dt;
641  zth_assert(t.isPositive());
642  m_t = t.ts();
643  }
644 
646  {
647  add(dt);
648  return *this;
649  }
650 
651  constexpr14 Timestamp operator+(TimeInterval const& dt) const noexcept
652  {
653  Timestamp t(*this);
654  return t += dt;
655  }
656 
658  {
659  add(-dt);
660  return *this;
661  }
662 
663  constexpr14 Timestamp operator-(TimeInterval const& dt) const noexcept
664  {
665  Timestamp t(*this);
666  return t -= dt;
667  }
668 
669  constexpr14 TimeInterval operator-(Timestamp const& rhs) const noexcept
670  {
671  return rhs.timeTo(*this);
672  }
673 
674  static constexpr Timestamp null() noexcept
675  {
676  return Timestamp();
677  }
678 
679  constexpr bool isNull() const noexcept
680  {
681  return m_t.tv_sec == 0 && m_t.tv_nsec == 0;
682  }
683 
684 private:
685  struct timespec m_t;
686 };
687 
688 # ifdef ZTH_OS_MAC
689 // Should be const, but the alias-trick does not work on OSX.
690 extern Timestamp /* const */ startTime;
691 # else
692 extern Timestamp const startTime;
693 # endif
694 
695 } // namespace zth
696 
697 #endif // __cplusplus
698 #endif // ZTH_TIME_H
Convenient wrapper around struct timespec that contains a time interval.
Definition: time.h:52
constexpr bool operator==(TimeInterval const &rhs) const noexcept
Definition: time.h:276
constexpr TimeInterval operator+(TimeInterval const &rhs) const noexcept
Definition: time.h:353
constexpr TimeInterval(T dt) noexcept
Definition: time.h:138
static constexpr TimeInterval from_s(T s)
Definition: time.h:146
TimeInterval(double dt)
Definition: time.h:121
constexpr bool operator>(TimeInterval const &rhs) const noexcept
Definition: time.h:282
constexpr TimeInterval operator-(TimeInterval const &rhs) const noexcept
Definition: time.h:366
static constexpr TimeInterval from_ms(T ms)
Definition: time.h:155
constexpr bool isInfinite() const noexcept
Definition: time.h:234
constexpr TimeInterval & operator*=(T x) noexcept
Definition: time.h:379
static constexpr TimeInterval from_ns(T ns)
Definition: time.h:175
constexpr TimeInterval & operator/=(T x) noexcept
Definition: time.h:392
constexpr void sub(TimeInterval const &t) noexcept
Definition: time.h:336
constexpr bool hasPassed() const noexcept
Definition: time.h:239
constexpr TimeInterval() noexcept
Definition: time.h:71
constexpr TimeInterval operator/(T x) const noexcept
Definition: time.h:399
constexpr void add(TimeInterval const &t) noexcept
Definition: time.h:302
constexpr void mul(T x) noexcept
Definition: time.h:342
static constexpr TimeInterval from_us(T us)
Definition: time.h:165
string str() const
Definition: time.h:404
constexpr bool operator<=(TimeInterval const &rhs) const noexcept
Definition: time.h:297
constexpr bool isPositive() const noexcept
Definition: time.h:224
constexpr struct timespec const & ts() const noexcept
Definition: time.h:244
constexpr bool operator>=(TimeInterval const &rhs) const noexcept
Definition: time.h:287
TimeInterval & operator=(TimeInterval const &t) noexcept
Definition: time.h:100
constexpr static TimeInterval infinity() noexcept
Definition: time.h:61
constexpr TimeInterval & operator-=(TimeInterval const &rhs) noexcept
Definition: time.h:360
constexpr TimeInterval(time_t s, long ns=0, bool negative=false) noexcept
Definition: time.h:78
constexpr double s() const noexcept
Definition: time.h:249
constexpr T s() const noexcept
Definition: time.h:255
constexpr bool isNegative() const noexcept
Definition: time.h:219
TimeInterval(long double dt)
Definition: time.h:129
static long const BILLION
Definition: time.h:59
constexpr bool isNull() const noexcept
Definition: time.h:229
constexpr bool operator<(TimeInterval const &rhs) const noexcept
Definition: time.h:292
constexpr bool isAbsBiggerThan(TimeInterval const &t) const noexcept
Definition: time.h:263
constexpr TimeInterval operator-() const noexcept
Definition: time.h:373
TimeInterval(float dt)
Definition: time.h:113
constexpr TimeInterval(TimeInterval const &t) noexcept
Definition: time.h:107
constexpr TimeInterval operator*(T x) const noexcept
Definition: time.h:386
constexpr TimeInterval(struct timespec const &ts) noexcept
Definition: time.h:95
constexpr bool isNormal() const noexcept
Definition: time.h:214
constexpr TimeInterval & operator+=(TimeInterval const &rhs) noexcept
Definition: time.h:347
constexpr bool isBiggerThan(TimeInterval const &t) const noexcept
Definition: time.h:269
Convenient wrapper around struct timespec that contains an absolute timestamp.
Definition: time.h:527
constexpr Timestamp & operator-=(TimeInterval const &dt) noexcept
Definition: time.h:657
constexpr Timestamp(struct timespec const &ts) noexcept
Definition: time.h:536
static Timestamp now()
Definition: time.h:554
constexpr TimeInterval operator-(Timestamp const &rhs) const noexcept
Definition: time.h:669
constexpr bool operator==(Timestamp const &rhs) const noexcept
Definition: time.h:588
constexpr Timestamp operator-(TimeInterval const &dt) const noexcept
Definition: time.h:663
constexpr bool isNull() const noexcept
Definition: time.h:679
constexpr struct timespec const & ts() const noexcept
Definition: time.h:563
Timestamp(TimeInterval const &ti)
Definition: time.h:548
constexpr bool operator>=(Timestamp const &rhs) const noexcept
Definition: time.h:609
constexpr bool isAfter(Timestamp const &t) const noexcept
Definition: time.h:578
constexpr bool operator!=(Timestamp const &rhs) const noexcept
Definition: time.h:593
constexpr bool operator<(Timestamp const &rhs) const noexcept
Definition: time.h:597
bool hasPassed() const noexcept
Definition: time.h:583
constexpr void add(TimeInterval const &dt) noexcept
Definition: time.h:637
constexpr bool operator>(Timestamp const &rhs) const noexcept
Definition: time.h:605
constexpr Timestamp(time_t sec, long nsec=0) noexcept
Definition: time.h:540
constexpr Timestamp() noexcept
Definition: time.h:531
constexpr bool isBefore(Timestamp const &t) const noexcept
Definition: time.h:572
constexpr Timestamp & operator+=(TimeInterval const &dt) noexcept
Definition: time.h:645
constexpr TimeInterval timeTo(Timestamp const &t) const
Definition: time.h:614
TimeInterval passed() const
Definition: time.h:620
constexpr bool operator<=(Timestamp const &rhs) const noexcept
Definition: time.h:601
constexpr Timestamp operator+(TimeInterval const &dt) const noexcept
Definition: time.h:651
Copy-on-write string.
Definition: util.h:344
#define ZTH_CLASS_NEW_DELETE(T)
Define new/delete operators for a class, which are allocator-aware.
Definition: allocator.h:114
#define constexpr14
Definition: macros.h:201
Definition: allocator.h:23
cow_string str< TimeInterval const & >(TimeInterval const &value)
Definition: time.h:467
Timestamp const startTime
Definition: time.cpp:191
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
Definition: util.h:503
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition: util.h:236