]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // (C) Copyright Howard Hinnant |
2 | // (C) Copyright 2011 Vicente J. Botet Escriba | |
3 | // Use, modification and distribution are subject to the Boost Software License, | |
4 | // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | |
5 | // http://www.boost.org/LICENSE_1_0.txt). | |
6 | // | |
7 | ||
8 | #ifndef BOOST_CHRONO_IO_DURATION_GET_HPP | |
9 | #define BOOST_CHRONO_IO_DURATION_GET_HPP | |
10 | ||
11 | #include <boost/chrono/config.hpp> | |
12 | #include <string> | |
13 | #include <boost/type_traits/is_scalar.hpp> | |
14 | #include <boost/utility/enable_if.hpp> | |
15 | #include <boost/type_traits/is_signed.hpp> | |
16 | #include <boost/mpl/if.hpp> | |
17 | #include <boost/integer/common_factor_rt.hpp> | |
18 | #include <boost/chrono/detail/scan_keyword.hpp> | |
19 | #include <boost/chrono/detail/no_warning/signed_unsigned_cmp.hpp> | |
20 | #include <boost/chrono/process_cpu_clocks.hpp> | |
21 | ||
22 | #include <boost/assert.hpp> | |
23 | #include <locale> | |
24 | ||
25 | /** | |
26 | * Duration formatting facet for input. | |
27 | */ | |
28 | namespace boost | |
29 | { | |
30 | namespace chrono | |
31 | { | |
32 | ||
33 | namespace detail | |
34 | { | |
35 | template <class Rep, bool = is_scalar<Rep>::value> | |
36 | struct duration_io_intermediate | |
37 | { | |
38 | typedef Rep type; | |
39 | }; | |
40 | ||
41 | template <class Rep> | |
42 | struct duration_io_intermediate<Rep, true> | |
43 | { | |
44 | typedef typename mpl::if_c<is_floating_point<Rep>::value, long double, typename mpl::if_c< | |
45 | is_signed<Rep>::value, long long, unsigned long long>::type>::type type; | |
46 | }; | |
47 | ||
48 | template <class Rep> | |
49 | struct duration_io_intermediate<process_times<Rep>, false> | |
50 | { | |
51 | typedef process_times<typename duration_io_intermediate<Rep>::type> type; | |
52 | }; | |
53 | ||
54 | template <typename intermediate_type> | |
55 | typename enable_if<is_integral<intermediate_type> , bool>::type reduce(intermediate_type& r, | |
56 | unsigned long long& den, std::ios_base::iostate& err) | |
57 | { | |
58 | typedef typename common_type<intermediate_type, unsigned long long>::type common_type_t; | |
59 | ||
60 | // Reduce r * num / den | |
61 | common_type_t t = integer::gcd<common_type_t>(common_type_t(r), common_type_t(den)); | |
62 | r /= t; | |
63 | den /= t; | |
64 | if (den != 1) | |
65 | { | |
66 | // Conversion to Period is integral and not exact | |
67 | err |= std::ios_base::failbit; | |
68 | return false; | |
69 | } | |
70 | return true; | |
71 | } | |
72 | template <typename intermediate_type> | |
73 | typename disable_if<is_integral<intermediate_type> , bool>::type reduce(intermediate_type&, unsigned long long&, | |
74 | std::ios_base::iostate&) | |
75 | { | |
76 | return true; | |
77 | } | |
78 | ||
79 | } | |
80 | ||
81 | /** | |
82 | * @c duration_get is used to parse a character sequence, extracting | |
83 | * components of a duration into a class duration. | |
84 | * Each get member parses a format as produced by a corresponding format specifier to time_put<>::put. | |
85 | * If the sequence being parsed matches the correct format, the | |
86 | * corresponding member of the class duration argument are set to the | |
87 | * value used to produce the sequence; | |
88 | * otherwise either an error is reported or unspecified values are assigned. | |
89 | * In other words, user confirmation is required for reliable parsing of | |
90 | * user-entered durations, but machine-generated formats can be parsed | |
91 | * reliably. This allows parsers to be aggressive about interpreting user | |
92 | * variations on standard formats. | |
93 | * | |
94 | * If the end iterator is reached during parsing of the get() member | |
95 | * function, the member sets std::ios_base::eofbit in err. | |
96 | */ | |
97 | template <class CharT, class InputIterator = std::istreambuf_iterator<CharT> > | |
98 | class duration_get: public std::locale::facet | |
99 | { | |
100 | public: | |
101 | /** | |
102 | * Type of character the facet is instantiated on. | |
103 | */ | |
104 | typedef CharT char_type; | |
105 | /** | |
106 | * Type of character string passed to member functions. | |
107 | */ | |
108 | typedef std::basic_string<CharT> string_type; | |
109 | /** | |
110 | * Type of iterator used to scan the character buffer. | |
111 | */ | |
112 | typedef InputIterator iter_type; | |
113 | ||
114 | /** | |
115 | * Construct a @c duration_get facet. | |
116 | * @param refs | |
117 | * @Effects Construct a @c duration_get facet. | |
118 | * If the @c refs argument is @c 0 then destruction of the object is | |
119 | * delegated to the @c locale, or locales, containing it. This allows | |
120 | * the user to ignore lifetime management issues. On the other had, | |
121 | * if @c refs is @c 1 then the object must be explicitly deleted; | |
122 | * the @c locale will not do so. In this case, the object can be | |
123 | * maintained across the lifetime of multiple locales. | |
124 | */ | |
125 | ||
126 | explicit duration_get(size_t refs = 0) : | |
127 | std::locale::facet(refs) | |
128 | { | |
129 | } | |
130 | ||
131 | /** | |
132 | * @param s start input stream iterator | |
133 | * @param end end input stream iterator | |
134 | * @param ios a reference to a ios_base | |
135 | * @param err the ios_base state | |
136 | * @param d the duration | |
137 | * @param pattern begin of the formatting pattern | |
138 | * @param pat_end end of the formatting pattern | |
139 | * | |
140 | * Requires: [pattern,pat_end) shall be a valid range. | |
141 | * | |
142 | * Effects: The function starts by evaluating err = std::ios_base::goodbit. | |
143 | * It then enters a loop, reading zero or more characters from s at | |
144 | * each iteration. Unless otherwise specified below, the loop | |
145 | * terminates when the first of the following conditions holds: | |
146 | * - The expression pattern == pat_end evaluates to true. | |
147 | * - The expression err == std::ios_base::goodbit evaluates to false. | |
148 | * - The expression s == end evaluates to true, in which case the | |
149 | * function evaluates err = std::ios_base::eofbit | std::ios_base::failbit. | |
150 | * - The next element of pattern is equal to '%', followed by a conversion | |
151 | * specifier character, format. | |
152 | * If the number of elements in the range [pattern,pat_end) is not | |
153 | * sufficient to unambiguously determine whether the conversion | |
154 | * specification is complete and valid, the function evaluates | |
155 | * err = std::ios_base::failbit. Otherwise, the function evaluates | |
156 | * s = get_value(s, end, ios, err, r) when the conversion specification is 'v' and | |
157 | * s = get_value(s, end, ios, err, rt) when the conversion specification is 'u'. | |
158 | * If err == std::ios_base::goodbit holds after | |
159 | * the evaluation of the expression, the function increments pattern to | |
160 | * point just past the end of the conversion specification and continues | |
161 | * looping. | |
162 | * - The expression isspace(*pattern, ios.getloc()) evaluates to true, in | |
163 | * which case the function first increments pattern until | |
164 | * pattern == pat_end || !isspace(*pattern, ios.getloc()) evaluates to true, | |
165 | * then advances s until s == end || !isspace(*s, ios.getloc()) is true, | |
166 | * and finally resumes looping. | |
167 | * - The next character read from s matches the element pointed to by | |
168 | * pattern in a case-insensitive comparison, in which case the function | |
169 | * evaluates ++pattern, ++s and continues looping. Otherwise, the function | |
170 | * evaluates err = std::ios_base::failbit. | |
171 | * | |
172 | * Once r and rt are retrieved, | |
173 | * Returns: s | |
174 | */ | |
175 | template <typename Rep, typename Period> | |
176 | iter_type get(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, | |
177 | duration<Rep, Period> &d, const char_type *pattern, const char_type *pat_end) const | |
178 | { | |
179 | if (std::has_facet<duration_units<CharT> >(ios.getloc())) | |
180 | { | |
181 | duration_units<CharT> const&facet = std::use_facet<duration_units<CharT> >(ios.getloc()); | |
182 | return get(facet, s, end, ios, err, d, pattern, pat_end); | |
183 | } | |
184 | else | |
185 | { | |
186 | duration_units_default<CharT> facet; | |
187 | return get(facet, s, end, ios, err, d, pattern, pat_end); | |
188 | } | |
189 | } | |
190 | ||
191 | template <typename Rep, typename Period> | |
192 | iter_type get(duration_units<CharT> const&facet, iter_type s, iter_type end, std::ios_base& ios, | |
193 | std::ios_base::iostate& err, duration<Rep, Period> &d, const char_type *pattern, const char_type *pat_end) const | |
194 | { | |
195 | ||
196 | typedef typename detail::duration_io_intermediate<Rep>::type intermediate_type; | |
197 | intermediate_type r; | |
198 | rt_ratio rt; | |
199 | bool value_found = false, unit_found = false; | |
200 | ||
201 | const std::ctype<char_type>& ct = std::use_facet<std::ctype<char_type> >(ios.getloc()); | |
202 | while (pattern != pat_end && err == std::ios_base::goodbit) | |
203 | { | |
204 | if (s == end) | |
205 | { | |
206 | err |= std::ios_base::eofbit; | |
207 | break; | |
208 | } | |
209 | if (ct.narrow(*pattern, 0) == '%') | |
210 | { | |
211 | if (++pattern == pat_end) | |
212 | { | |
213 | err |= std::ios_base::failbit; | |
214 | return s; | |
215 | } | |
216 | char cmd = ct.narrow(*pattern, 0); | |
217 | switch (cmd) | |
218 | { | |
219 | case 'v': | |
220 | { | |
221 | if (value_found) | |
222 | { | |
223 | err |= std::ios_base::failbit; | |
224 | return s; | |
225 | } | |
226 | value_found = true; | |
227 | s = get_value(s, end, ios, err, r); | |
228 | if (err & (std::ios_base::badbit | std::ios_base::failbit)) | |
229 | { | |
230 | return s; | |
231 | } | |
232 | break; | |
233 | } | |
234 | case 'u': | |
235 | { | |
236 | if (unit_found) | |
237 | { | |
238 | err |= std::ios_base::failbit; | |
239 | return s; | |
240 | } | |
241 | unit_found = true; | |
242 | s = get_unit(facet, s, end, ios, err, rt); | |
243 | if (err & (std::ios_base::badbit | std::ios_base::failbit)) | |
244 | { | |
245 | return s; | |
246 | } | |
247 | break; | |
248 | } | |
249 | default: | |
250 | BOOST_ASSERT(false && "Boost::Chrono internal error."); | |
251 | break; | |
252 | } | |
253 | ||
254 | ++pattern; | |
255 | } | |
256 | else if (ct.is(std::ctype_base::space, *pattern)) | |
257 | { | |
258 | for (++pattern; pattern != pat_end && ct.is(std::ctype_base::space, *pattern); ++pattern) | |
259 | ; | |
260 | for (; s != end && ct.is(std::ctype_base::space, *s); ++s) | |
261 | ; | |
262 | } | |
263 | else if (ct.toupper(*s) == ct.toupper(*pattern)) | |
264 | { | |
265 | ++s; | |
266 | ++pattern; | |
267 | } | |
268 | else | |
269 | { | |
270 | err |= std::ios_base::failbit; | |
271 | return s; | |
272 | } | |
273 | ||
274 | } | |
275 | ||
276 | unsigned long long num = rt.num; | |
277 | unsigned long long den = rt.den; | |
278 | ||
279 | // r should be multiplied by (num/den) / Period | |
280 | // Reduce (num/den) / Period to lowest terms | |
281 | unsigned long long gcd_n1_n2 = integer::gcd<unsigned long long>(num, Period::num); | |
282 | unsigned long long gcd_d1_d2 = integer::gcd<unsigned long long>(den, Period::den); | |
283 | num /= gcd_n1_n2; | |
284 | den /= gcd_d1_d2; | |
285 | unsigned long long n2 = Period::num / gcd_n1_n2; | |
286 | unsigned long long d2 = Period::den / gcd_d1_d2; | |
287 | if (num > (std::numeric_limits<unsigned long long>::max)() / d2 || den | |
288 | > (std::numeric_limits<unsigned long long>::max)() / n2) | |
289 | { | |
290 | // (num/den) / Period overflows | |
291 | err |= std::ios_base::failbit; | |
292 | return s; | |
293 | } | |
294 | num *= d2; | |
295 | den *= n2; | |
296 | ||
297 | typedef typename common_type<intermediate_type, unsigned long long>::type common_type_t; | |
298 | ||
299 | // num / den is now factor to multiply by r | |
300 | if (!detail::reduce(r, den, err)) return s; | |
301 | ||
302 | if (chrono::detail::gt(r, ( (duration_values<common_type_t>::max)() / num))) | |
303 | { | |
304 | // Conversion to Period overflowed | |
305 | err |= std::ios_base::failbit; | |
306 | return s; | |
307 | } | |
308 | common_type_t t = r * num; | |
309 | t /= den; | |
310 | if (t > duration_values<common_type_t>::zero()) | |
311 | { | |
312 | Rep pt = t; | |
313 | if ( (duration_values<Rep>::max)() < pt) | |
314 | { | |
315 | // Conversion to Period overflowed | |
316 | err |= std::ios_base::failbit; | |
317 | return s; | |
318 | } | |
319 | } | |
320 | // Success! Store it. | |
321 | r = Rep(t); | |
322 | d = duration<Rep, Period> (r); | |
323 | ||
324 | return s; | |
325 | } | |
326 | ||
327 | /** | |
328 | * | |
329 | * @param s start input stream iterator | |
330 | * @param end end input stream iterator | |
331 | * @param ios a reference to a ios_base | |
332 | * @param err the ios_base state | |
333 | * @param d the duration | |
334 | * Stores the duration pattern from the @c duration_unit facet in let say @c str. Last as if | |
335 | * @code | |
336 | * return get(s, end, ios, err, ios, d, str.data(), str.data() + str.size()); | |
337 | * @codeend | |
338 | * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name | |
339 | */ | |
340 | template <typename Rep, typename Period> | |
341 | iter_type get(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, | |
342 | duration<Rep, Period> & d) const | |
343 | { | |
344 | if (std::has_facet<duration_units<CharT> >(ios.getloc())) | |
345 | { | |
346 | duration_units<CharT> const&facet = std::use_facet<duration_units<CharT> >(ios.getloc()); | |
347 | std::basic_string<CharT> str = facet.get_pattern(); | |
348 | return get(facet, s, end, ios, err, d, str.data(), str.data() + str.size()); | |
349 | } | |
350 | else | |
351 | { | |
352 | duration_units_default<CharT> facet; | |
353 | std::basic_string<CharT> str = facet.get_pattern(); | |
354 | return get(facet, s, end, ios, err, d, str.data(), str.data() + str.size()); | |
355 | } | |
356 | } | |
357 | ||
358 | /** | |
359 | * | |
360 | * @param s start input stream iterator | |
361 | * @param end end input stream iterator | |
362 | * @param ios a reference to a ios_base | |
363 | * @param err the ios_base state | |
364 | * @param r a reference to the duration representation. | |
365 | * @Effects As if | |
366 | * @code | |
367 | * return std::use_facet<std::num_get<cahr_type, iter_type> >(ios.getloc()).get(s, end, ios, err, r); | |
368 | * @endcode | |
369 | * | |
370 | * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name | |
371 | */ | |
372 | template <typename Rep> | |
373 | iter_type get_value(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, Rep& r) const | |
374 | { | |
375 | return std::use_facet<std::num_get<CharT, iter_type> >(ios.getloc()).get(s, end, ios, err, r); | |
376 | } | |
377 | template <typename Rep> | |
378 | iter_type get_value(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, process_times<Rep>& r) const | |
379 | { | |
380 | if (s == end) { | |
381 | err |= std::ios_base::eofbit; | |
382 | return s; | |
383 | } else if (*s != '{') { // mandatory '{' | |
384 | err |= std::ios_base::failbit; | |
385 | return s; | |
386 | } | |
387 | ++s; | |
388 | s = std::use_facet<std::num_get<CharT, iter_type> >(ios.getloc()).get(s, end, ios, err, r.real); | |
389 | if (s == end) { | |
390 | err |= std::ios_base::eofbit; | |
391 | return s; | |
392 | } else if (*s != ';') { // mandatory ';' | |
393 | err |= std::ios_base::failbit; | |
394 | return s; | |
395 | } | |
396 | ++s; | |
397 | s = std::use_facet<std::num_get<CharT, iter_type> >(ios.getloc()).get(s, end, ios, err, r.user); | |
398 | if (s == end) { | |
399 | err |= std::ios_base::eofbit; | |
400 | return s; | |
401 | } else if (*s != ';') { // mandatory ';' | |
402 | err |= std::ios_base::failbit; | |
403 | return s; | |
404 | } | |
405 | ++s; | |
406 | s = std::use_facet<std::num_get<CharT, iter_type> >(ios.getloc()).get(s, end, ios, err, r.system); | |
407 | if (s == end) { | |
408 | err |= std::ios_base::eofbit; | |
409 | return s; | |
410 | } else if (*s != '}') { // mandatory '}' | |
411 | err |= std::ios_base::failbit; | |
412 | return s; | |
413 | } | |
414 | return s; | |
415 | } | |
416 | ||
417 | /** | |
418 | * | |
419 | * @param s start input stream iterator | |
420 | * @param e end input stream iterator | |
421 | * @param ios a reference to a ios_base | |
422 | * @param err the ios_base state | |
423 | * @param rt a reference to the duration run-time ratio. | |
424 | * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name | |
425 | */ | |
426 | iter_type get_unit(iter_type i, iter_type e, std::ios_base& is, std::ios_base::iostate& err, rt_ratio &rt) const | |
427 | { | |
428 | if (std::has_facet<duration_units<CharT> >(is.getloc())) | |
429 | { | |
430 | return get_unit(std::use_facet<duration_units<CharT> >(is.getloc()), i, e, is, err, rt); | |
431 | } | |
432 | else | |
433 | { | |
434 | duration_units_default<CharT> facet; | |
435 | return get_unit(facet, i, e, is, err, rt); | |
436 | } | |
437 | } | |
438 | ||
439 | ||
440 | iter_type get_unit(duration_units<CharT> const &facet, iter_type i, iter_type e, std::ios_base& is, | |
441 | std::ios_base::iostate& err, rt_ratio &rt) const | |
442 | { | |
443 | ||
444 | if (*i == '[') | |
445 | { | |
446 | // parse [N/D]s or [N/D]second or [N/D]seconds format | |
447 | ++i; | |
448 | i = std::use_facet<std::num_get<CharT, iter_type> >(is.getloc()).get(i, e, is, err, rt.num); | |
449 | if ( (err & std::ios_base::failbit) != 0) | |
450 | { | |
451 | return i; | |
452 | } | |
453 | ||
454 | if (i == e) | |
455 | { | |
456 | err |= std::ios_base::failbit; | |
457 | return i; | |
458 | } | |
459 | CharT x = *i++; | |
460 | if (x != '/') | |
461 | { | |
462 | err |= std::ios_base::failbit; | |
463 | return i; | |
464 | } | |
465 | i = std::use_facet<std::num_get<CharT, iter_type> >(is.getloc()).get(i, e, is, err, rt.den); | |
466 | if ( (err & std::ios_base::failbit) != 0) | |
467 | { | |
468 | return i; | |
469 | } | |
470 | if (i == e) | |
471 | { | |
472 | err |= std::ios_base::failbit; | |
473 | return i; | |
474 | } | |
475 | if (*i != ']') | |
476 | { | |
477 | err |= std::ios_base::failbit; | |
478 | return i; | |
479 | } | |
480 | ++i; | |
481 | if (i == e) | |
482 | { | |
483 | err |= std::ios_base::failbit; | |
484 | return i; | |
485 | } | |
486 | // parse s or second or seconds | |
487 | return do_get_n_d_valid_unit(facet, i, e, is, err); | |
488 | } | |
489 | else | |
490 | { | |
491 | return do_get_valid_unit(facet, i, e, is, err, rt); | |
492 | } | |
493 | } | |
494 | ||
495 | /** | |
496 | * Unique identifier for this type of facet. | |
497 | */ | |
498 | static std::locale::id id; | |
499 | ||
500 | /** | |
501 | * @Effects Destroy the facet | |
502 | */ | |
503 | ~duration_get() | |
504 | { | |
505 | } | |
506 | ||
507 | protected: | |
508 | ||
509 | /** | |
510 | * Extracts the run-time ratio associated to the duration when it is given in prefix form. | |
511 | * | |
512 | * This is an extension point of this facet so that we can take in account other periods that can have a useful | |
513 | * translation in other contexts, as e.g. days and weeks. | |
514 | * | |
515 | * @param facet the duration_units facet | |
516 | * @param i start input stream iterator. | |
517 | * @param e end input stream iterator. | |
518 | * @param ios a reference to a ios_base. | |
519 | * @param err the ios_base state. | |
520 | * @return @c s | |
521 | */ | |
522 | iter_type do_get_n_d_valid_unit(duration_units<CharT> const &facet, iter_type i, iter_type e, | |
523 | std::ios_base&, std::ios_base::iostate& err) const | |
524 | { | |
525 | // parse SI name, short or long | |
526 | ||
527 | const string_type* units = facet.get_n_d_valid_units_start(); | |
528 | const string_type* units_end = facet.get_n_d_valid_units_end(); | |
529 | ||
530 | const string_type* k = chrono_detail::scan_keyword(i, e, units, units_end, | |
531 | //~ std::use_facet<std::ctype<CharT> >(loc), | |
532 | err); | |
533 | if (err & (std::ios_base::badbit | std::ios_base::failbit)) | |
534 | { | |
535 | return i; | |
536 | } | |
537 | if (!facet.match_n_d_valid_unit(k)) | |
538 | { | |
539 | err |= std::ios_base::failbit; | |
540 | } | |
541 | return i; | |
542 | } | |
543 | ||
544 | /** | |
545 | * Extracts the run-time ratio associated to the duration when it is given in prefix form. | |
546 | * | |
547 | * This is an extension point of this facet so that we can take in account other periods that can have a useful | |
548 | * translation in other contexts, as e.g. days and weeks. | |
549 | * | |
550 | * @param facet the duration_units facet | |
551 | * @param i start input stream iterator. | |
552 | * @param e end input stream iterator. | |
553 | * @param ios a reference to a ios_base. | |
554 | * @param err the ios_base state. | |
555 | * @param rt a reference to the duration run-time ratio. | |
556 | * @Effects | |
557 | * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name. | |
558 | */ | |
559 | iter_type do_get_valid_unit(duration_units<CharT> const &facet, iter_type i, iter_type e, | |
560 | std::ios_base&, std::ios_base::iostate& err, rt_ratio &rt) const | |
561 | { | |
562 | // parse SI name, short or long | |
563 | ||
564 | const string_type* units = facet.get_valid_units_start(); | |
565 | const string_type* units_end = facet.get_valid_units_end(); | |
566 | ||
567 | err = std::ios_base::goodbit; | |
568 | const string_type* k = chrono_detail::scan_keyword(i, e, units, units_end, | |
569 | //~ std::use_facet<std::ctype<CharT> >(loc), | |
570 | err); | |
571 | if (err & (std::ios_base::badbit | std::ios_base::failbit)) | |
572 | { | |
573 | return i; | |
574 | } | |
575 | if (!facet.match_valid_unit(k, rt)) | |
576 | { | |
577 | err |= std::ios_base::failbit; | |
578 | } | |
579 | return i; | |
580 | } | |
581 | }; | |
582 | ||
583 | /** | |
584 | * Unique identifier for this type of facet. | |
585 | */ | |
586 | template <class CharT, class InputIterator> | |
587 | std::locale::id duration_get<CharT, InputIterator>::id; | |
588 | ||
589 | } // chrono | |
590 | } | |
591 | // boost | |
592 | ||
593 | #endif // header |