source: mainline/uspace/lib/cpp/include/__bits/chrono.hpp@ bd41ac52

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bd41ac52 was bd41ac52, checked in by Jakub Jermar <jakub@…>, 7 years ago

Get rid of sys/time.h

This commit moves the POSIX-like time functionality from libc's
sys/time.h to libposix and introduces C11-like or HelenOS-specific
interfaces to libc.

Specifically, use of sys/time.h, struct timeval, suseconds_t and
gettimeofday is replaced by time.h (C11), struct timespec (C11), usec_t
(HelenOS) and getuptime / getrealtime (HelenOS).

Also attempt to fix the implementation of clock() to return microseconds
(clocks) rather than processor cycles and move it to libc.

  • Property mode set to 100644
File size: 23.3 KB
Line 
1/*
2 * Copyright (c) 2018 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_BITS_CHRONO
30#define LIBCPP_BITS_CHRONO
31
32#include <ctime>
33#include <limits>
34#include <ratio>
35#include <type_traits>
36
37namespace std::chrono
38{
39 /**
40 * 20.12.5, class template duration:
41 */
42
43 // Forward declarations.
44 template<class Rep, class Period = ratio<1>>
45 class duration;
46
47 template<class ToDuration, class Rep, class Period>
48 constexpr ToDuration duration_cast(const duration<Rep, Period>& dur);
49
50 template<class Rep>
51 struct duration_values;
52
53 template<class Rep, class Period>
54 class duration
55 {
56 public:
57 using rep = Rep;
58 using period = Period;
59
60 /**
61 * 20.12.5.1, construct/copy/destroy:
62 */
63
64 constexpr duration() = default;
65
66 // TODO: check remarks to these two constructors, need is_convertible
67 template<class Rep2>
68 constexpr explicit duration(const Rep2& r)
69 : rep_{r}
70 { /* DUMMY BODY */ }
71
72 template<class Rep2, class Period2>
73 constexpr duration(const duration<Rep2, Period2>& other)
74 : rep_{duration_cast<duration>(other).count()}
75 { /* DUMMY BODY */ }
76
77 ~duration() = default;
78
79 duration(const duration&) = default;
80
81 duration& operator=(const duration&) = default;
82
83 /**
84 * 20.12.5.2, observer:
85 */
86
87 constexpr rep count() const
88 {
89 return rep_;
90 }
91
92 /**
93 * 20.12.5.3, arithmetic:
94 */
95
96 constexpr duration operator+() const
97 {
98 return *this;
99 }
100
101 constexpr duration operator-() const
102 {
103 return duration{-rep_};
104 }
105
106 duration& operator++()
107 {
108 ++rep_;
109
110 return *this;
111 }
112
113 duration operator++(int)
114 {
115 return duration{rep_++};
116 }
117
118 duration& operator--()
119 {
120 --rep_;
121
122 *this;
123 }
124
125 duration operator--(int)
126 {
127 return duration{rep_--};
128 }
129
130 duration& operator+=(const duration& rhs)
131 {
132 rep_ += rhs.count();
133
134 return *this;
135 }
136
137 duration& operator-=(const duration& rhs)
138 {
139 rep_ -= rhs.count();
140
141 return *this;
142 }
143
144 duration& operator*=(const rep& rhs)
145 {
146 rep_ *= rhs;
147
148 return *this;
149 }
150
151 duration& operator/=(const rep& rhs)
152 {
153 rep_ /= rhs;
154
155 return *this;
156 }
157
158 duration& operator%=(const rep& rhs)
159 {
160 rep_ %= rhs;
161
162 return *this;
163 }
164
165 duration& operator%=(const duration& rhs)
166 {
167 rep_ %= rhs.count();
168
169 return *this;
170 }
171
172 /**
173 * 20.12.5.4, special values:
174 */
175
176 static constexpr duration zero()
177 {
178 return duration{duration_values<rep>::zero()};
179 }
180
181 static constexpr duration min()
182 {
183 return duration{duration_values<rep>::min()};
184 }
185
186 static constexpr duration max()
187 {
188 return duration{duration_values<rep>::max()};
189 }
190
191 private:
192 rep rep_;
193 };
194
195 /**
196 * 20.12.6, class template time_point:
197 */
198
199 template<class Clock, class Duration = typename Clock::duration>
200 class time_point
201 {
202 public:
203 using clock = Clock;
204 using duration = Duration;
205 using rep = typename duration::rep;
206 using period = typename duration::period;
207
208 /**
209 * 20.12.6.1, construct:
210 */
211
212 constexpr time_point()
213 : duration_{duration::zero()}
214 { /* DUMMY BODY */ }
215
216 constexpr explicit time_point(const duration& d)
217 : duration_{d}
218 { /* DUMMY BODY */ }
219
220 // TODO: see remark to this constuctor
221 template<class Duration2>
222 constexpr time_point(const time_point<clock, Duration2>& other)
223 : duration_{static_cast<duration>(other.time_since_epoch())}
224 { /* DUMMY BODY */ }
225
226 /**
227 * 20.12.6.2, observer:
228 */
229
230 constexpr duration time_since_epoch() const
231 {
232 return duration_;
233 }
234
235 /**
236 * 20.12.6.3, arithmetic:
237 */
238
239 time_point& operator+=(const duration& rhs)
240 {
241 duration_ += rhs;
242
243 return *this;
244 }
245
246 time_point& operator-=(const duration& rhs)
247 {
248 duration_ -= rhs;
249
250 return *this;
251 }
252
253 /**
254 * 20.12.6.4, special values:
255 */
256
257 static constexpr time_point min()
258 {
259 return time_point{duration::min()};
260 }
261
262 static constexpr time_point max()
263 {
264 return time_point{duration::max()};
265 }
266
267 private:
268 duration duration_;
269 };
270}
271
272namespace std
273{
274 /**
275 * 20.12.4.3, common_type specializations:
276 */
277
278 template<class Rep1, class Period1, class Rep2, class Period2>
279 struct common_type<chrono::duration<Rep1, Period1>, chrono::duration<Rep2, Period2>>
280 {
281 using type = chrono::duration<
282 common_type_t<Rep1, Rep2>,
283 ratio<aux::gcd_v<Period1::num, Period2::num>, aux::lcm_v<Period1::den, Period2::den>>
284 >;
285 };
286
287 template<class Clock, class Duration1, class Duration2>
288 struct common_type<chrono::time_point<Clock, Duration1>, chrono::time_point<Clock, Duration2>>
289 {
290 using type = chrono::time_point<Clock, common_type_t<Duration1, Duration2>>;
291 };
292}
293
294namespace std::chrono
295{
296 /**
297 * 20.12.4, customization traits:
298 */
299
300 template<class Rep>
301 struct treat_as_floating_point: is_floating_point<Rep>
302 { /* DUMMY BODY */ };
303
304 template<class Rep>
305 struct duration_values
306 {
307 static constexpr Rep zero()
308 {
309 // Note: Using Rep(0) instead of Rep{} is intentional, do not change.
310 return Rep(0);
311 }
312
313 static constexpr Rep min()
314 {
315 return numeric_limits<Rep>::lowest();
316 }
317
318 static constexpr Rep max()
319 {
320 return numeric_limits<Rep>::max();
321 }
322 };
323
324 /**
325 * 20.12.5.5, duration arithmetic:
326 */
327
328 template<class Rep1, class Period1, class Rep2, class Period2>
329 constexpr common_type_t<duration<Rep1, Period1>, duration<Rep2, Period2>>
330 operator+(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs)
331 {
332 using CD = common_type_t<duration<Rep1, Period1>, duration<Rep2, Period2>>;
333 return CD(CD(lhs.count()) + CD(rhs.count()));
334 }
335
336 template<class Rep1, class Period1, class Rep2, class Period2>
337 constexpr common_type_t<duration<Rep1, Period1>, duration<Rep2, Period2>>
338 operator-(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs)
339 {
340 using CD = common_type_t<duration<Rep1, Period1>, duration<Rep2, Period2>>;
341 return CD(CD(lhs).count() - CD(rhs).count());
342 }
343
344 // TODO: This shall not participate in overloading if Rep2 is not implicitly
345 // convertible to CR(Rep1, Rep2) -> CR(A, B) defined in standard as
346 // common_type_t<A, B>.
347 template<class Rep1, class Period, class Rep2>
348 constexpr duration<common_type_t<Rep1, Rep2>, Period>
349 operator*(const duration<Rep1, Period>& dur, const Rep2& rep)
350 {
351 using CD = duration<common_type_t<Rep1, Rep2>, Period>;
352 return CD(CD(dur).count() * rep);
353 }
354
355 // TODO: This shall not participate in overloading if Rep2 is not implicitly
356 // convertible to CR(Rep1, Rep2) -> CR(A, B) defined in standard as
357 // common_type_t<A, B>.
358 template<class Rep1, class Period, class Rep2>
359 constexpr duration<common_type_t<Rep1, Rep2>, Period>
360 operator*(const Rep2& rep, const duration<Rep1, Period>& dur)
361 {
362 return dur * rep;
363 }
364
365 // TODO: This shall not participate in overloading if Rep2 is not implicitly
366 // convertible to CR(Rep1, Rep2) -> CR(A, B) defined in standard as
367 // common_type_t<A, B>.
368 template<class Rep1, class Period, class Rep2>
369 constexpr duration<common_type_t<Rep1, Rep2>, Period>
370 operator/(const duration<Rep1, Period>& dur, const Rep2& rep)
371 {
372 using CD = duration<common_type_t<Rep1, Rep2>, Period>;
373 return CD(CD(dur).count() / rep);
374 }
375
376 template<class Rep1, class Period1, class Rep2, class Period2>
377 constexpr common_type_t<Rep1, Rep2>
378 operator/(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs)
379 {
380 using CD = common_type_t<Rep1, Rep2>;
381 return CD(lhs).count() / CD(rhs).count();
382 }
383
384 // TODO: This shall not participate in overloading if Rep2 is not implicitly
385 // convertible to CR(Rep1, Rep2) -> CR(A, B) defined in standard as
386 // common_type_t<A, B>.
387 template<class Rep1, class Period, class Rep2>
388 constexpr duration<common_type_t<Rep1, Rep2>, Period>
389 operator%(const duration<Rep1, Period>& dur, const Rep2& rep)
390 {
391 using CD = duration<common_type_t<Rep1, Rep2>, Period>;
392 return CD(CD(dur).count() / rep);
393 }
394
395 template<class Rep1, class Period1, class Rep2, class Period2>
396 constexpr common_type_t<duration<Rep1, Period1>, duration<Rep2, Period2>>
397 operator%(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs)
398 {
399 using CD = common_type_t<duration<Rep1, Period1>, duration<Rep2, Period2>>;
400 return CD(CD(lhs).count() % CD(rhs).count());
401 }
402
403 /**
404 * 20.12.5.6, duration comparisons:
405 */
406
407 template<class Rep1, class Period1, class Rep2, class Period2>
408 constexpr bool operator==(const duration<Rep1, Period1>& lhs,
409 const duration<Rep2, Period2>& rhs)
410 {
411 using CT = common_type_t<duration<Rep1, Period1>, duration<Rep2, Period2>>;
412 return CT(lhs).count() == CT(rhs).count();
413 }
414
415 template<class Rep1, class Period1, class Rep2, class Period2>
416 constexpr bool operator!=(const duration<Rep1, Period1>& lhs,
417 const duration<Rep2, Period2>& rhs)
418 {
419 return !(lhs == rhs);
420 }
421
422 template<class Rep1, class Period1, class Rep2, class Period2>
423 constexpr bool operator<(const duration<Rep1, Period1>& lhs,
424 const duration<Rep2, Period2>& rhs)
425 {
426 using CT = common_type_t<duration<Rep1, Period1>, duration<Rep2, Period2>>;
427 return CT(lhs).count() < CT(rhs).count();
428 }
429
430 template<class Rep1, class Period1, class Rep2, class Period2>
431 constexpr bool operator<=(const duration<Rep1, Period1>& lhs,
432 const duration<Rep2, Period2>& rhs)
433 {
434 return !(rhs < lhs);
435 }
436
437 template<class Rep1, class Period1, class Rep2, class Period2>
438 constexpr bool operator>(const duration<Rep1, Period1>& lhs,
439 const duration<Rep2, Period2>& rhs)
440 {
441 return rhs < lhs;
442 }
443
444 template<class Rep1, class Period1, class Rep2, class Period2>
445 constexpr bool operator>=(const duration<Rep1, Period1>& lhs,
446 const duration<Rep2, Period2>& rhs)
447 {
448 return !(lhs < rhs);
449 }
450
451 /**
452 * 20.12.5.7, duration cast:
453 */
454
455 // TODO: This function should not participate in overloading
456 // unless ToDuration is an instantiation of duration.
457 template<class ToDuration, class Rep, class Period>
458 constexpr ToDuration duration_cast(const duration<Rep, Period>& dur)
459 {
460 using CF = ratio_divide<Period, typename ToDuration::period>;
461 using CR = typename common_type<typename ToDuration::rep, Rep, intmax_t>::type;
462
463 using to_rep = typename ToDuration::rep;
464
465 if constexpr (CF::num == 1 && CF::den == 1)
466 return ToDuration(static_cast<to_rep>(dur.count()));
467 else if constexpr (CF::num != 1 && CF::den == 1)
468 {
469 return ToDuration(
470 static_cast<to_rep>(
471 static_cast<CR>(dur.count()) * static_cast<CR>(CF::num)
472 )
473 );
474 }
475 else if constexpr (CF::num == 1 && CF::den != 1)
476 {
477 return ToDuration(
478 static_cast<to_rep>(
479 static_cast<CR>(dur.count()) / static_cast<CR>(CF::den)
480 )
481 );
482 }
483 else
484 {
485 return ToDuration(
486 static_cast<to_rep>(
487 static_cast<CR>(dur.count()) * static_cast<CR>(CF::num)
488 / static_cast<CR>(CF::den)
489 )
490 );
491 }
492 }
493
494 // convenience typedefs
495 using nanoseconds = duration<int64_t, nano>;
496 using microseconds = duration<int64_t, micro>;
497 using milliseconds = duration<int64_t, milli>;
498 using seconds = duration<int64_t>;
499 using minutes = duration<int32_t, ratio<60>>;
500 using hours = duration<int32_t, ratio<3600>>;
501
502 /**
503 * 20.12.6.5, time_point arithmetic:
504 */
505
506 template<class Clock, class Duration1, class Rep2, class Period2>
507 constexpr time_point<Clock, common_type_t<Duration1, duration<Rep2, Period2>>>
508 operator+(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs)
509 {
510 using CT = time_point<Clock, common_type_t<Duration1, duration<Rep2, Period2>>>;
511 return CT(lhs.time_since_epoch() + rhs);
512 }
513
514 template<class Rep1, class Period1, class Clock, class Duration2>
515 constexpr time_point<Clock, common_type_t<duration<Rep1, Period1>, Duration2>>
516 operator+(const duration<Rep1, Period1>& lhs, const time_point<Clock, Duration2>& rhs)
517 {
518 return rhs + lhs;
519 }
520
521 template<class Clock, class Duration1, class Rep2, class Period2>
522 constexpr time_point<Clock, common_type_t<Duration1, duration<Rep2, Period2>>>
523 operator-(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs)
524 {
525 return lhs + (-rhs);
526 }
527
528 template<class Clock, class Duration1, class Duration2>
529 constexpr common_type_t<Duration1, Duration2>
530 operator-(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs)
531 {
532 return lhs.time_since_epoch() - rhs.time_since_epoch();
533 }
534
535 /**
536 * 20.12.6.6, time_point comparisons:
537 */
538
539 template<class Clock, class Duration1, class Duration2>
540 constexpr bool operator==(const time_point<Clock, Duration1>& lhs,
541 const time_point<Clock, Duration2>& rhs)
542 {
543 return lhs.time_since_epoch() == rhs.time_since_epoch();
544 }
545
546 template<class Clock, class Duration1, class Duration2>
547 constexpr bool operator!=(const time_point<Clock, Duration1>& lhs,
548 const time_point<Clock, Duration2>& rhs)
549 {
550 return !(lhs == rhs);
551 }
552
553 template<class Clock, class Duration1, class Duration2>
554 constexpr bool operator<(const time_point<Clock, Duration1>& lhs,
555 const time_point<Clock, Duration2>& rhs)
556 {
557 return lhs.time_since_epoch() < rhs.time_since_epoch();
558 }
559
560 template<class Clock, class Duration1, class Duration2>
561 constexpr bool operator<=(const time_point<Clock, Duration1>& lhs,
562 const time_point<Clock, Duration2>& rhs)
563 {
564 return !(rhs < lhs);
565 }
566
567 template<class Clock, class Duration1, class Duration2>
568 constexpr bool operator>(const time_point<Clock, Duration1>& lhs,
569 const time_point<Clock, Duration2>& rhs)
570 {
571 return rhs < lhs;
572 }
573
574 template<class Clock, class Duration1, class Duration2>
575 constexpr bool operator>=(const time_point<Clock, Duration1>& lhs,
576 const time_point<Clock, Duration2>& rhs)
577 {
578 return !(lhs < rhs);
579 }
580
581 /**
582 * 20.12.6.7, time_point cast:
583 */
584
585 // TODO: This function should not participate in overloading
586 // unless ToDuration is an instantiation of duration.
587 template<class ToDuration, class Clock, class Duration>
588 constexpr time_point<Clock, ToDuration>
589 time_point_cast(const time_point<Clock, Duration>& tp)
590 {
591 return time_point<Clock, ToDuration>(
592 duration_cast<ToDuration>(tp.time_since_epoch())
593 );
594 }
595
596 /**
597 * 20.12.7, clocks:
598 */
599
600 class system_clock
601 {
602 public:
603 using rep = int64_t;
604 using period = micro;
605 using duration = chrono::duration<rep, period>;
606 using time_point = chrono::time_point<system_clock>;
607
608 // TODO: is it steady?
609 static constexpr bool is_steady = true;
610
611 static time_point now()
612 {
613 hel::timespec ts{};
614 hel::getrealtime(&ts);
615
616 rep time = NSEC2USEC(ts.tv_nsec);
617 time += (ts.tv_sec * 1'000'000ul);
618
619 return time_point{duration{time - epoch_usecs}};
620 }
621
622 static time_t to_time_t(const time_point& tp)
623 {
624 /* auto dur = tp.time_since_epoch(); */
625 /* auto time_t_dur = duration_cast<chrono::duration<time_t, seconds>>(dur); */
626
627 /* return time_t{time_t_dur.count()}; */
628 return time_t{};
629 }
630
631 static time_point from_time_t(time_t tt)
632 {
633 /* auto time_t_dur = chrono::duration<time_t, seconds>{tt}; */
634 /* auto dur = duration_cast<duration>(time_t_dur); */
635
636 /* return time_point{duration_cast<duration>(chrono::duration<time_t, seconds>{tt})}; */
637 return time_point{};
638 }
639
640 private:
641 static constexpr rep epoch_usecs{11644473600ul * 1'000'000ul};
642 };
643
644 class steady_clock
645 {
646 public:
647 using rep = int64_t;
648 using period = micro;
649 using duration = chrono::duration<rep, period>;
650 using time_point = chrono::time_point<steady_clock>;
651
652 static constexpr bool is_steady = true;
653
654 static time_point now()
655 {
656 hel::timespec ts{};
657 hel::getuptime(&ts);
658
659 rep time = NSEC2USEC(ts.tv_nsec);
660 time += (ts.tv_sec * 1'000'000ul);
661
662 return time_point{duration{time}};
663 }
664 };
665
666 using high_resolution_clock = system_clock;
667}
668
669namespace std
670{
671 inline namespace literals
672 {
673 inline namespace chrono_literals
674 {
675 /**
676 * 20.125.8, suffixes for duration literals:
677 */
678
679 /**
680 * Note: According to the standard, literal suffixes that do not
681 * start with an underscore are reserved for future standardization,
682 * but since we are implementing the standard, we're going to ignore it.
683 * This should work (according to their documentation) work for clang,
684 * but that should be tested.
685 */
686#pragma GCC diagnostic push
687#pragma GCC diagnostic ignored "-Wliteral-suffix"
688
689 constexpr chrono::hours operator ""h(unsigned long long hrs)
690 {
691 return chrono::hours{static_cast<chrono::hours::rep>(hrs)};
692 }
693
694 constexpr chrono::duration<long double, ratio<3600>> operator ""h(long double hrs)
695 {
696 return chrono::duration<long double, ratio<3600>>{hrs};
697 }
698
699 constexpr chrono::minutes operator ""m(unsigned long long mins)
700 {
701 return chrono::minutes{static_cast<chrono::minutes::rep>(mins)};
702 }
703
704 constexpr chrono::duration<long double, ratio<60>> operator ""m(long double mins)
705 {
706 return chrono::duration<long double, ratio<60>>{mins};
707 }
708
709 constexpr chrono::seconds operator ""s(unsigned long long secs)
710 {
711 return chrono::seconds{static_cast<chrono::seconds::rep>(secs)};
712 }
713
714 constexpr chrono::duration<long double, ratio<1>> operator ""s(long double secs)
715 {
716 return chrono::duration<long double, ratio<1>>{secs};
717 }
718
719 constexpr chrono::milliseconds operator ""ms(unsigned long long msecs)
720 {
721 return chrono::milliseconds{static_cast<chrono::milliseconds::rep>(msecs)};
722 }
723
724 constexpr chrono::duration<long double, milli> operator ""ms(long double msecs)
725 {
726 return chrono::duration<long double, milli>{msecs};
727 }
728
729 constexpr chrono::microseconds operator ""us(unsigned long long usecs)
730 {
731 return chrono::microseconds{static_cast<chrono::microseconds::rep>(usecs)};
732 }
733
734 constexpr chrono::duration<long double, micro> operator ""us(long double usecs)
735 {
736 return chrono::duration<long double, micro>{usecs};
737 }
738
739 constexpr chrono::nanoseconds operator ""ns(unsigned long long nsecs)
740 {
741 return chrono::nanoseconds{static_cast<chrono::nanoseconds::rep>(nsecs)};
742 }
743
744 constexpr chrono::duration<long double, nano> operator ""ns(long double nsecs)
745 {
746 return chrono::duration<long double, nano>{nsecs};
747 }
748
749#pragma GCC diagnostic pop
750 }
751 }
752
753 namespace chrono
754 {
755 using namespace literals::chrono_literals;
756 }
757}
758
759#endif
Note: See TracBrowser for help on using the repository browser.