From 97097f632b50f1dae400fa1401a0d67f6aba3513 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Sun, 4 Oct 2020 15:52:10 -0700 Subject: [PATCH 1/8] : [time.duration.io] output --- stl/inc/chrono | 94 +++++++++++++++ tests/std/test.lst | 1 + .../duration_output_tests.h | 111 ++++++++++++++++++ .../env.lst | 4 + .../test.cpp | 10 ++ 5 files changed, 220 insertions(+) create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_io/env.lst create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp diff --git a/stl/inc/chrono b/stl/inc/chrono index 8405b26be58..d5cfe08ac61 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -10,6 +10,7 @@ #if _STL_COMPILER_PREPROCESSOR #include #include +#include #include #include #include @@ -617,6 +618,99 @@ namespace chrono { }; using high_resolution_clock = steady_clock; + + // [time.duration.io] + + class _Units_suffix_helper { + public: + template + _NODISCARD static constexpr const _CharT* _Get_literal() { + for (const auto& _Entry : _Suffixes) { + if (_Entry._Num == _Period::num && _Entry._Den == _Period::den) { + return _STD get(_Entry._Name); + } + } + return nullptr; + } + + template + _NODISCARD static _CharT* _Get_general(_CharT* _Rnext, intmax_t _Num, intmax_t _Den) { + // Returns the head pointer of the string, built in reverse. + _STL_INTERNAL_CHECK(_Num > 0 && _Den > 0); + *--_Rnext = '\0'; + *--_Rnext = 's'; + *--_Rnext = ']'; + if (_Den != 1) { + _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Den)); + *--_Rnext = '/'; + } + _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Num)); + *--_Rnext = '['; + return _Rnext; + } + + private: + struct _Suf_names { + const intmax_t _Num; + const intmax_t _Den; + const pair _Name; + }; + +#define _LITERAL_SUFFIX(_TYPE, _SUFFIX) \ + { _TYPE::num, _TYPE::den, _STD make_pair(_SUFFIX, L##_SUFFIX) } +#define _LITERAL_SUFFIX2(_TYPE, _NARROW_SUFFIX, _WIDE_SUFFIX) \ + { _TYPE::num, _TYPE::den, _STD make_pair(_NARROW_SUFFIX, _WIDE_SUFFIX) } + + static constexpr _Suf_names _Suffixes[]{ + // clang-format off + _LITERAL_SUFFIX(atto, "as"), + _LITERAL_SUFFIX(femto, "fs"), + _LITERAL_SUFFIX(pico, "ps"), + _LITERAL_SUFFIX(nano, "ns"), + _LITERAL_SUFFIX2(micro, "us", L"\u00b5s"), + _LITERAL_SUFFIX(milli, "ms"), + _LITERAL_SUFFIX(centi, "cs"), + _LITERAL_SUFFIX(deci, "ds"), + _LITERAL_SUFFIX(seconds::period, "s"), + _LITERAL_SUFFIX(deca, "das"), + _LITERAL_SUFFIX(hecto, "hs"), + _LITERAL_SUFFIX(kilo, "ks"), + _LITERAL_SUFFIX(mega, "Ms"), + _LITERAL_SUFFIX(giga, "Gs"), + _LITERAL_SUFFIX(tera, "Ts"), + _LITERAL_SUFFIX(peta, "Ps"), + _LITERAL_SUFFIX(exa, "Es"), + _LITERAL_SUFFIX(minutes::period, "min"), + _LITERAL_SUFFIX(hours::period, "h"), + _LITERAL_SUFFIX(ratio<86400>, "d") + // clang-format on + }; + +#undef _LITERAL_SUFFIX +#undef _LITERAL_SUFFIX2 + }; + + template + basic_ostream<_CharT, _Traits>& operator<<( + basic_ostream<_CharT, _Traits>& _Os, const duration<_Rep, _Period>& _Dur) { + basic_ostringstream<_CharT, _Traits> _Sstr; + _Sstr.flags(_Os.flags()); + _Sstr.imbue(_Os.getloc()); + _Sstr.precision(_Os.precision()); + _Sstr << _Dur.count(); + + constexpr auto _Suffix = _Units_suffix_helper::_Get_literal<_CharT, _Period>(); + if _CONSTEXPR_IF (_Suffix != nullptr) { + _Sstr << _Suffix; + } else { + _CharT _Buffer[2 * (numeric_limits::digits10 + 1) + 5] = {}; // 2 numbers + "[/]s\0" + const _CharT* _Begin = + _Units_suffix_helper::_Get_general<_CharT>(_STD end(_Buffer), _Period::num, _Period::den); + _Sstr << _Begin; + } + + return _Os << _Sstr.str(); + } } // namespace chrono // HELPERS diff --git a/tests/std/test.lst b/tests/std/test.lst index c681576094b..2593fb1b95c 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -221,6 +221,7 @@ tests\P0220R1_searchers tests\P0220R1_string_view tests\P0325R4_to_array tests\P0339R6_polymorphic_allocator +tests\P0355R7_calendars_and_time_zones_io tests\P0356R5_bind_front tests\P0357R3_supporting_incomplete_types_in_reference_wrapper tests\P0414R2_shared_ptr_for_arrays diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h b/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h new file mode 100644 index 00000000000..853d34ca0a7 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include +#include +#include +#include + +namespace duration_output { + using namespace std; + using namespace std::chrono; + + template + bool test_duration(const duration& d, const CharT* expected) { + basic_ostringstream ss; + + ss << d; + return ss.str() == expected; + } + +#define WIDEN(TYPE, STR) get(make_pair(STR, L##STR)); + + template + bool test_duration_stream_manip() { + basic_stringstream ss; + const duration d{0.140625}; + ss.precision(3); + ss << d; + ss.setf(ios_base::scientific, ios_base::floatfield); + ss << ' ' << d; + + basic_string expected = WIDEN(CharT, "0.141s 1.406e-01s"); + +#if !defined(_DLL) || ((_ITERATOR_DEBUG_LEVEL == 0) != defined(_DEBUG)) + // Using dynamic linking, only release mode supports IDL == 0. + struct comma : numpunct { + CharT do_decimal_point() const { + return ','; + } + }; + + ss.imbue(locale(ss.getloc(), new comma)); + ss << ' ' << d; + expected += WIDEN(CharT, " 1,406e-01s"); +#endif + + return ss.str() == expected; + } + +#undef WIDEN + + void test() { + assert(test_duration(duration{1}, "1as")); + assert(test_duration(duration{2}, "2fs")); + assert(test_duration(duration{3}, "3ps")); + assert(test_duration(duration{42}, "42ns")); + assert(test_duration(duration{42}, "42us")); + assert(test_duration(duration{42}, "42ms")); + assert(test_duration(duration{42}, "42cs")); + assert(test_duration(duration{42}, "42ds")); + assert(test_duration(duration>{42}, "42s")); + assert(test_duration(duration{42}, "42das")); + assert(test_duration(duration{42}, "42hs")); + assert(test_duration(duration{42}, "42ks")); + assert(test_duration(duration{42}, "42Ms")); + assert(test_duration(duration{42}, "42Gs")); + assert(test_duration(duration{42}, "42Ts")); + assert(test_duration(duration{42}, "42Ps")); + assert(test_duration(duration{42}, "42Es")); + assert(test_duration(duration>{42}, "42min")); + assert(test_duration(duration>{42}, "42h")); + assert(test_duration(duration>{42}, "42d")); + + assert(test_duration(duration>{24}, "24[2]s")); + assert(test_duration(duration>{24}, "24[1/2]s")); + assert(test_duration(duration>{24}, "24[22/7]s")); + + assert(test_duration(duration{0.140625}, "0.140625s")); + assert(test_duration_stream_manip()); + + assert(test_duration(duration{1}, L"1as")); + assert(test_duration(duration{2}, L"2fs")); + assert(test_duration(duration{3}, L"3ps")); + assert(test_duration(duration{42}, L"42ns")); + assert(test_duration(duration{42}, L"42\u00b5\u0073")); + assert(test_duration(duration{42}, "42ms")); + assert(test_duration(duration{42}, L"42cs")); + assert(test_duration(duration{42}, L"42ds")); + assert(test_duration(duration>{42}, L"42s")); + assert(test_duration(duration{42}, L"42das")); + assert(test_duration(duration{42}, L"42hs")); + assert(test_duration(duration{42}, L"42ks")); + assert(test_duration(duration{42}, L"42Ms")); + assert(test_duration(duration{42}, L"42Gs")); + assert(test_duration(duration{42}, L"42Ts")); + assert(test_duration(duration{42}, L"42Ps")); + assert(test_duration(duration{42}, L"42Es")); + assert(test_duration(duration>{42}, L"42min")); + assert(test_duration(duration>{42}, L"42h")); + assert(test_duration(duration>{42}, L"42d")); + + assert(test_duration(duration>{24}, L"24[2]s")); + assert(test_duration(duration>{24}, L"24[1/2]s")); + assert(test_duration(duration>{24}, L"24[22/7]s")); + + assert(test_duration(duration{0.140625}, L"0.140625s")); + assert(test_duration_stream_manip()); + } +} // namespace duration_output diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_io/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp new file mode 100644 index 00000000000..b58a50cfc94 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "duration_output_tests.h" + + +int main() { + duration_output::test(); + return 0; +} From 35db89aac23ec8f7f66702e931a34621f2872a1b Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Wed, 7 Oct 2020 15:39:18 -0700 Subject: [PATCH 2/8] Add C++20 guard --- stl/inc/chrono | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index d5cfe08ac61..6c47df5fc4a 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -619,8 +619,8 @@ namespace chrono { using high_resolution_clock = steady_clock; +#ifdef _HAS_CXX20 // [time.duration.io] - class _Units_suffix_helper { public: template @@ -711,6 +711,7 @@ namespace chrono { return _Os << _Sstr.str(); } +#endif // _HAS_CXX20 } // namespace chrono // HELPERS From b20091548e2140df90803fce0c1109082daa8ebe Mon Sep 17 00:00:00 2001 From: MattStephanson <68978048+MattStephanson@users.noreply.github.com> Date: Tue, 20 Oct 2020 21:28:45 -0700 Subject: [PATCH 3/8] Apply suggestions from code review Co-authored-by: mnatsuhara <46756417+mnatsuhara@users.noreply.github.com> --- stl/inc/chrono | 1 + .../P0355R7_calendars_and_time_zones_io/duration_output_tests.h | 2 +- tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 6c47df5fc4a..349531f6b72 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -644,6 +644,7 @@ namespace chrono { _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Den)); *--_Rnext = '/'; } + _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Num)); *--_Rnext = '['; return _Rnext; diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h b/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h index 853d34ca0a7..66c902c014f 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h @@ -44,7 +44,7 @@ namespace duration_output { ss.imbue(locale(ss.getloc(), new comma)); ss << ' ' << d; expected += WIDEN(CharT, " 1,406e-01s"); -#endif +#endif // !defined(_DLL) || ((_ITERATOR_DEBUG_LEVEL == 0) != defined(_DEBUG)) return ss.str() == expected; } diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp index b58a50cfc94..808d44b3cb0 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp @@ -3,7 +3,6 @@ #include "duration_output_tests.h" - int main() { duration_output::test(); return 0; From 14a7e8d77b58eae178b361e8b4fbe533f56962b9 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Tue, 20 Oct 2020 22:05:09 -0700 Subject: [PATCH 4/8] Suggestions from code review --- stl/inc/chrono | 12 +++++------- stl/inc/string | 2 +- .../duration_output_tests.h | 5 ++++- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 349531f6b72..85de53567d1 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -644,7 +644,7 @@ namespace chrono { _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Den)); *--_Rnext = '/'; } - + _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Num)); *--_Rnext = '['; return _Rnext; @@ -659,8 +659,6 @@ namespace chrono { #define _LITERAL_SUFFIX(_TYPE, _SUFFIX) \ { _TYPE::num, _TYPE::den, _STD make_pair(_SUFFIX, L##_SUFFIX) } -#define _LITERAL_SUFFIX2(_TYPE, _NARROW_SUFFIX, _WIDE_SUFFIX) \ - { _TYPE::num, _TYPE::den, _STD make_pair(_NARROW_SUFFIX, _WIDE_SUFFIX) } static constexpr _Suf_names _Suffixes[]{ // clang-format off @@ -668,7 +666,7 @@ namespace chrono { _LITERAL_SUFFIX(femto, "fs"), _LITERAL_SUFFIX(pico, "ps"), _LITERAL_SUFFIX(nano, "ns"), - _LITERAL_SUFFIX2(micro, "us", L"\u00b5s"), + _LITERAL_SUFFIX(micro, "us"), _LITERAL_SUFFIX(milli, "ms"), _LITERAL_SUFFIX(centi, "cs"), _LITERAL_SUFFIX(deci, "ds"), @@ -701,13 +699,13 @@ namespace chrono { _Sstr << _Dur.count(); constexpr auto _Suffix = _Units_suffix_helper::_Get_literal<_CharT, _Period>(); - if _CONSTEXPR_IF (_Suffix != nullptr) { - _Sstr << _Suffix; - } else { + if _CONSTEXPR_IF (_Suffix == nullptr) { _CharT _Buffer[2 * (numeric_limits::digits10 + 1) + 5] = {}; // 2 numbers + "[/]s\0" const _CharT* _Begin = _Units_suffix_helper::_Get_general<_CharT>(_STD end(_Buffer), _Period::num, _Period::den); _Sstr << _Begin; + } else { + _Sstr << _Suffix; } return _Os << _Sstr.str(); diff --git a/stl/inc/string b/stl/inc/string index 09802c114e1..72d3cebb2d8 100644 --- a/stl/inc/string +++ b/stl/inc/string @@ -448,7 +448,7 @@ inline long double stold(const wstring& _Str, size_t* _Idx = nullptr) { // conve return _Ans; } -// HELPERS FOR to_string AND to_wstring +// HELPERS FOR to_string AND to_wstring AND operator<<(duration) template _Elem* _UIntegral_to_buff(_Elem* _RNext, _UTy _UVal) { // format _UVal into buffer *ending at* _RNext static_assert(is_unsigned_v<_UTy>, "_UTy must be unsigned"); diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h b/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h index 66c902c014f..1daf7b5577f 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h @@ -52,6 +52,7 @@ namespace duration_output { #undef WIDEN void test() { + using LongRatio = ratio; assert(test_duration(duration{1}, "1as")); assert(test_duration(duration{2}, "2fs")); assert(test_duration(duration{3}, "3ps")); @@ -76,6 +77,7 @@ namespace duration_output { assert(test_duration(duration>{24}, "24[2]s")); assert(test_duration(duration>{24}, "24[1/2]s")); assert(test_duration(duration>{24}, "24[22/7]s")); + assert(test_duration(duration{24}, "24[9223372036854775806/9223372036854775807]s")); assert(test_duration(duration{0.140625}, "0.140625s")); assert(test_duration_stream_manip()); @@ -84,7 +86,7 @@ namespace duration_output { assert(test_duration(duration{2}, L"2fs")); assert(test_duration(duration{3}, L"3ps")); assert(test_duration(duration{42}, L"42ns")); - assert(test_duration(duration{42}, L"42\u00b5\u0073")); + assert(test_duration(duration{42}, L"42us")); assert(test_duration(duration{42}, "42ms")); assert(test_duration(duration{42}, L"42cs")); assert(test_duration(duration{42}, L"42ds")); @@ -104,6 +106,7 @@ namespace duration_output { assert(test_duration(duration>{24}, L"24[2]s")); assert(test_duration(duration>{24}, L"24[1/2]s")); assert(test_duration(duration>{24}, L"24[22/7]s")); + assert(test_duration(duration{24}, L"24[9223372036854775806/9223372036854775807]s")); assert(test_duration(duration{0.140625}, L"0.140625s")); assert(test_duration_stream_manip()); From ca416bd71058f7e50e49df06758afc96532acce1 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Tue, 27 Oct 2020 17:05:52 -0700 Subject: [PATCH 5/8] Reorganize test code into single source file --- .../duration_output_tests.h | 114 ------------------ .../test.cpp | 110 ++++++++++++++++- 2 files changed, 108 insertions(+), 116 deletions(-) delete mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h b/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h deleted file mode 100644 index 1daf7b5577f..00000000000 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_io/duration_output_tests.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#pragma once - -#include -#include -#include -#include - -namespace duration_output { - using namespace std; - using namespace std::chrono; - - template - bool test_duration(const duration& d, const CharT* expected) { - basic_ostringstream ss; - - ss << d; - return ss.str() == expected; - } - -#define WIDEN(TYPE, STR) get(make_pair(STR, L##STR)); - - template - bool test_duration_stream_manip() { - basic_stringstream ss; - const duration d{0.140625}; - ss.precision(3); - ss << d; - ss.setf(ios_base::scientific, ios_base::floatfield); - ss << ' ' << d; - - basic_string expected = WIDEN(CharT, "0.141s 1.406e-01s"); - -#if !defined(_DLL) || ((_ITERATOR_DEBUG_LEVEL == 0) != defined(_DEBUG)) - // Using dynamic linking, only release mode supports IDL == 0. - struct comma : numpunct { - CharT do_decimal_point() const { - return ','; - } - }; - - ss.imbue(locale(ss.getloc(), new comma)); - ss << ' ' << d; - expected += WIDEN(CharT, " 1,406e-01s"); -#endif // !defined(_DLL) || ((_ITERATOR_DEBUG_LEVEL == 0) != defined(_DEBUG)) - - return ss.str() == expected; - } - -#undef WIDEN - - void test() { - using LongRatio = ratio; - assert(test_duration(duration{1}, "1as")); - assert(test_duration(duration{2}, "2fs")); - assert(test_duration(duration{3}, "3ps")); - assert(test_duration(duration{42}, "42ns")); - assert(test_duration(duration{42}, "42us")); - assert(test_duration(duration{42}, "42ms")); - assert(test_duration(duration{42}, "42cs")); - assert(test_duration(duration{42}, "42ds")); - assert(test_duration(duration>{42}, "42s")); - assert(test_duration(duration{42}, "42das")); - assert(test_duration(duration{42}, "42hs")); - assert(test_duration(duration{42}, "42ks")); - assert(test_duration(duration{42}, "42Ms")); - assert(test_duration(duration{42}, "42Gs")); - assert(test_duration(duration{42}, "42Ts")); - assert(test_duration(duration{42}, "42Ps")); - assert(test_duration(duration{42}, "42Es")); - assert(test_duration(duration>{42}, "42min")); - assert(test_duration(duration>{42}, "42h")); - assert(test_duration(duration>{42}, "42d")); - - assert(test_duration(duration>{24}, "24[2]s")); - assert(test_duration(duration>{24}, "24[1/2]s")); - assert(test_duration(duration>{24}, "24[22/7]s")); - assert(test_duration(duration{24}, "24[9223372036854775806/9223372036854775807]s")); - - assert(test_duration(duration{0.140625}, "0.140625s")); - assert(test_duration_stream_manip()); - - assert(test_duration(duration{1}, L"1as")); - assert(test_duration(duration{2}, L"2fs")); - assert(test_duration(duration{3}, L"3ps")); - assert(test_duration(duration{42}, L"42ns")); - assert(test_duration(duration{42}, L"42us")); - assert(test_duration(duration{42}, "42ms")); - assert(test_duration(duration{42}, L"42cs")); - assert(test_duration(duration{42}, L"42ds")); - assert(test_duration(duration>{42}, L"42s")); - assert(test_duration(duration{42}, L"42das")); - assert(test_duration(duration{42}, L"42hs")); - assert(test_duration(duration{42}, L"42ks")); - assert(test_duration(duration{42}, L"42Ms")); - assert(test_duration(duration{42}, L"42Gs")); - assert(test_duration(duration{42}, L"42Ts")); - assert(test_duration(duration{42}, L"42Ps")); - assert(test_duration(duration{42}, L"42Es")); - assert(test_duration(duration>{42}, L"42min")); - assert(test_duration(duration>{42}, L"42h")); - assert(test_duration(duration>{42}, L"42d")); - - assert(test_duration(duration>{24}, L"24[2]s")); - assert(test_duration(duration>{24}, L"24[1/2]s")); - assert(test_duration(duration>{24}, L"24[22/7]s")); - assert(test_duration(duration{24}, L"24[9223372036854775806/9223372036854775807]s")); - - assert(test_duration(duration{0.140625}, L"0.140625s")); - assert(test_duration_stream_manip()); - } -} // namespace duration_output diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp index 808d44b3cb0..c5f3bc719b1 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp @@ -1,9 +1,115 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include "duration_output_tests.h" +#include +#include +#include +#include + +using namespace std; +using chrono::duration; + +template +bool test_duration_basic_out(const duration& d, const CharT* expected) { + basic_ostringstream ss; + + ss << d; + return ss.str() == expected; +} + +#define WIDEN(TYPE, STR) get(make_pair(STR, L##STR)); + +template +bool test_duration_locale_out() { + basic_stringstream ss; + const duration d{0.140625}; + ss.precision(3); + ss << d; + ss.setf(ios_base::scientific, ios_base::floatfield); + ss << ' ' << d; + + basic_string expected = WIDEN(CharT, "0.141s 1.406e-01s"); + +#if !defined(_DLL) || ((_ITERATOR_DEBUG_LEVEL == 0) != defined(_DEBUG)) + // Using dynamic linking, only release mode supports IDL == 0. + struct comma : numpunct { + CharT do_decimal_point() const { + return ','; + } + }; + + ss.imbue(locale(ss.getloc(), new comma)); + ss << ' ' << d; + expected += WIDEN(CharT, " 1,406e-01s"); +#endif // !defined(_DLL) || ((_ITERATOR_DEBUG_LEVEL == 0) != defined(_DEBUG)) + + return ss.str() == expected; +} + +#undef WIDEN + +void test_duration_output() { + using LongRatio = ratio; + assert(test_duration_basic_out(duration{1}, "1as")); + assert(test_duration_basic_out(duration{2}, "2fs")); + assert(test_duration_basic_out(duration{3}, "3ps")); + assert(test_duration_basic_out(duration{42}, "42ns")); + assert(test_duration_basic_out(duration{42}, "42us")); + assert(test_duration_basic_out(duration{42}, "42ms")); + assert(test_duration_basic_out(duration{42}, "42cs")); + assert(test_duration_basic_out(duration{42}, "42ds")); + assert(test_duration_basic_out(duration>{42}, "42s")); + assert(test_duration_basic_out(duration{42}, "42das")); + assert(test_duration_basic_out(duration{42}, "42hs")); + assert(test_duration_basic_out(duration{42}, "42ks")); + assert(test_duration_basic_out(duration{42}, "42Ms")); + assert(test_duration_basic_out(duration{42}, "42Gs")); + assert(test_duration_basic_out(duration{42}, "42Ts")); + assert(test_duration_basic_out(duration{42}, "42Ps")); + assert(test_duration_basic_out(duration{42}, "42Es")); + assert(test_duration_basic_out(duration>{42}, "42min")); + assert(test_duration_basic_out(duration>{42}, "42h")); + assert(test_duration_basic_out(duration>{42}, "42d")); + + assert(test_duration_basic_out(duration>{24}, "24[2]s")); + assert(test_duration_basic_out(duration>{24}, "24[1/2]s")); + assert(test_duration_basic_out(duration>{24}, "24[22/7]s")); + assert(test_duration_basic_out(duration{24}, "24[9223372036854775806/9223372036854775807]s")); + + assert(test_duration_basic_out(duration{0.140625}, "0.140625s")); + assert(test_duration_locale_out()); + + assert(test_duration_basic_out(duration{1}, L"1as")); + assert(test_duration_basic_out(duration{2}, L"2fs")); + assert(test_duration_basic_out(duration{3}, L"3ps")); + assert(test_duration_basic_out(duration{42}, L"42ns")); + assert(test_duration_basic_out(duration{42}, L"42us")); + assert(test_duration_basic_out(duration{42}, "42ms")); + assert(test_duration_basic_out(duration{42}, L"42cs")); + assert(test_duration_basic_out(duration{42}, L"42ds")); + assert(test_duration_basic_out(duration>{42}, L"42s")); + assert(test_duration_basic_out(duration{42}, L"42das")); + assert(test_duration_basic_out(duration{42}, L"42hs")); + assert(test_duration_basic_out(duration{42}, L"42ks")); + assert(test_duration_basic_out(duration{42}, L"42Ms")); + assert(test_duration_basic_out(duration{42}, L"42Gs")); + assert(test_duration_basic_out(duration{42}, L"42Ts")); + assert(test_duration_basic_out(duration{42}, L"42Ps")); + assert(test_duration_basic_out(duration{42}, L"42Es")); + assert(test_duration_basic_out(duration>{42}, L"42min")); + assert(test_duration_basic_out(duration>{42}, L"42h")); + assert(test_duration_basic_out(duration>{42}, L"42d")); + + assert(test_duration_basic_out(duration>{24}, L"24[2]s")); + assert(test_duration_basic_out(duration>{24}, L"24[1/2]s")); + assert(test_duration_basic_out(duration>{24}, L"24[22/7]s")); + assert(test_duration_basic_out(duration{24}, L"24[9223372036854775806/9223372036854775807]s")); + + assert(test_duration_basic_out(duration{0.140625}, L"0.140625s")); + assert(test_duration_locale_out()); +} int main() { - duration_output::test(); + test_duration_output(); return 0; } From 05f67acec7e82a3a54f5da786f247e302abe44b9 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Wed, 28 Oct 2020 14:53:18 -0700 Subject: [PATCH 6/8] Don't need to undef the macro that I deleted --- stl/inc/chrono | 1 - 1 file changed, 1 deletion(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 85de53567d1..3733c3d1ddd 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -686,7 +686,6 @@ namespace chrono { }; #undef _LITERAL_SUFFIX -#undef _LITERAL_SUFFIX2 }; template From 0a19f92efe87269eb2fdca0d8e09ec3828b9c6e6 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 4 Nov 2020 19:18:47 -0800 Subject: [PATCH 7/8] Code review feedback. --- stl/inc/chrono | 16 +++++++------- .../test.cpp | 22 ++++++++++++++----- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 3733c3d1ddd..97aecfc2740 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -619,7 +619,7 @@ namespace chrono { using high_resolution_clock = steady_clock; -#ifdef _HAS_CXX20 +#if _HAS_CXX20 // [time.duration.io] class _Units_suffix_helper { public: @@ -634,7 +634,7 @@ namespace chrono { } template - _NODISCARD static _CharT* _Get_general(_CharT* _Rnext, intmax_t _Num, intmax_t _Den) { + _NODISCARD static _CharT* _Get_general(_CharT* _Rnext, const intmax_t _Num, const intmax_t _Den) { // Returns the head pointer of the string, built in reverse. _STL_INTERNAL_CHECK(_Num > 0 && _Den > 0); *--_Rnext = '\0'; @@ -657,11 +657,12 @@ namespace chrono { const pair _Name; }; + // clang-format off #define _LITERAL_SUFFIX(_TYPE, _SUFFIX) \ - { _TYPE::num, _TYPE::den, _STD make_pair(_SUFFIX, L##_SUFFIX) } + { _TYPE::num, _TYPE::den, pair{_SUFFIX, L##_SUFFIX} } + // clang-format on static constexpr _Suf_names _Suffixes[]{ - // clang-format off _LITERAL_SUFFIX(atto, "as"), _LITERAL_SUFFIX(femto, "fs"), _LITERAL_SUFFIX(pico, "ps"), @@ -681,8 +682,7 @@ namespace chrono { _LITERAL_SUFFIX(exa, "Es"), _LITERAL_SUFFIX(minutes::period, "min"), _LITERAL_SUFFIX(hours::period, "h"), - _LITERAL_SUFFIX(ratio<86400>, "d") - // clang-format on + _LITERAL_SUFFIX(ratio<86400>, "d"), }; #undef _LITERAL_SUFFIX @@ -698,9 +698,9 @@ namespace chrono { _Sstr << _Dur.count(); constexpr auto _Suffix = _Units_suffix_helper::_Get_literal<_CharT, _Period>(); - if _CONSTEXPR_IF (_Suffix == nullptr) { + if constexpr (_Suffix == nullptr) { _CharT _Buffer[2 * (numeric_limits::digits10 + 1) + 5] = {}; // 2 numbers + "[/]s\0" - const _CharT* _Begin = + const _CharT* const _Begin = _Units_suffix_helper::_Get_general<_CharT>(_STD end(_Buffer), _Period::num, _Period::den); _Sstr << _Begin; } else { diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp index c5f3bc719b1..fefbba69291 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp @@ -3,8 +3,12 @@ #include #include +#include #include +#include #include +#include +#include using namespace std; using chrono::duration; @@ -22,7 +26,7 @@ bool test_duration_basic_out(const duration& d, const CharT* expect template bool test_duration_locale_out() { basic_stringstream ss; - const duration d{0.140625}; + const duration d{0.140625}; ss.precision(3); ss << d; ss.setf(ios_base::scientific, ios_base::floatfield); @@ -30,8 +34,14 @@ bool test_duration_locale_out() { basic_string expected = WIDEN(CharT, "0.141s 1.406e-01s"); -#if !defined(_DLL) || ((_ITERATOR_DEBUG_LEVEL == 0) != defined(_DEBUG)) - // Using dynamic linking, only release mode supports IDL == 0. +#ifdef _DEBUG +#define DEFAULT_IDL_SETTING 2 +#else +#define DEFAULT_IDL_SETTING 0 +#endif + +#if !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING + // When linking dynamically, user-defined facets are incompatible with non-default _ITERATOR_DEBUG_LEVEL settings. struct comma : numpunct { CharT do_decimal_point() const { return ','; @@ -41,7 +51,7 @@ bool test_duration_locale_out() { ss.imbue(locale(ss.getloc(), new comma)); ss << ' ' << d; expected += WIDEN(CharT, " 1,406e-01s"); -#endif // !defined(_DLL) || ((_ITERATOR_DEBUG_LEVEL == 0) != defined(_DEBUG)) +#endif // !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING return ss.str() == expected; } @@ -76,7 +86,7 @@ void test_duration_output() { assert(test_duration_basic_out(duration>{24}, "24[22/7]s")); assert(test_duration_basic_out(duration{24}, "24[9223372036854775806/9223372036854775807]s")); - assert(test_duration_basic_out(duration{0.140625}, "0.140625s")); + assert(test_duration_basic_out(duration{0.140625}, "0.140625s")); assert(test_duration_locale_out()); assert(test_duration_basic_out(duration{1}, L"1as")); @@ -105,7 +115,7 @@ void test_duration_output() { assert(test_duration_basic_out(duration>{24}, L"24[22/7]s")); assert(test_duration_basic_out(duration{24}, L"24[9223372036854775806/9223372036854775807]s")); - assert(test_duration_basic_out(duration{0.140625}, L"0.140625s")); + assert(test_duration_basic_out(duration{0.140625}, L"0.140625s")); assert(test_duration_locale_out()); } From 87a12a7b69fe72554e92d6ad37bc9df574e9edbd Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 6 Nov 2020 04:12:59 -0800 Subject: [PATCH 8/8] Rework the literal unit suffix table. --- stl/inc/chrono | 114 ++++++++++++++++++++++--------------------------- 1 file changed, 52 insertions(+), 62 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 97aecfc2740..b19aa83f7cd 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -621,72 +621,62 @@ namespace chrono { #if _HAS_CXX20 // [time.duration.io] - class _Units_suffix_helper { - public: - template - _NODISCARD static constexpr const _CharT* _Get_literal() { - for (const auto& _Entry : _Suffixes) { - if (_Entry._Num == _Period::num && _Entry._Den == _Period::den) { - return _STD get(_Entry._Name); - } - } + +#define _IF_PERIOD_RETURN_SUFFIX_ELSE(_TYPE, _SUFFIX) \ + if constexpr (is_same_v<_Period, _TYPE>) { \ + if constexpr (is_same_v<_CharT, char>) { \ + return _SUFFIX; \ + } else { \ + return L##_SUFFIX; \ + } \ + } else + + template + _NODISCARD constexpr const _CharT* _Get_literal_unit_suffix() { + _IF_PERIOD_RETURN_SUFFIX_ELSE(atto, "as") + _IF_PERIOD_RETURN_SUFFIX_ELSE(femto, "fs") + _IF_PERIOD_RETURN_SUFFIX_ELSE(pico, "ps") + _IF_PERIOD_RETURN_SUFFIX_ELSE(nano, "ns") + _IF_PERIOD_RETURN_SUFFIX_ELSE(micro, "us") + _IF_PERIOD_RETURN_SUFFIX_ELSE(milli, "ms") + _IF_PERIOD_RETURN_SUFFIX_ELSE(centi, "cs") + _IF_PERIOD_RETURN_SUFFIX_ELSE(deci, "ds") + _IF_PERIOD_RETURN_SUFFIX_ELSE(seconds::period, "s") + _IF_PERIOD_RETURN_SUFFIX_ELSE(deca, "das") + _IF_PERIOD_RETURN_SUFFIX_ELSE(hecto, "hs") + _IF_PERIOD_RETURN_SUFFIX_ELSE(kilo, "ks") + _IF_PERIOD_RETURN_SUFFIX_ELSE(mega, "Ms") + _IF_PERIOD_RETURN_SUFFIX_ELSE(giga, "Gs") + _IF_PERIOD_RETURN_SUFFIX_ELSE(tera, "Ts") + _IF_PERIOD_RETURN_SUFFIX_ELSE(peta, "Ps") + _IF_PERIOD_RETURN_SUFFIX_ELSE(exa, "Es") + _IF_PERIOD_RETURN_SUFFIX_ELSE(minutes::period, "min") + _IF_PERIOD_RETURN_SUFFIX_ELSE(hours::period, "h") + _IF_PERIOD_RETURN_SUFFIX_ELSE(ratio<86400>, "d") + + { return nullptr; } + } - template - _NODISCARD static _CharT* _Get_general(_CharT* _Rnext, const intmax_t _Num, const intmax_t _Den) { - // Returns the head pointer of the string, built in reverse. - _STL_INTERNAL_CHECK(_Num > 0 && _Den > 0); - *--_Rnext = '\0'; - *--_Rnext = 's'; - *--_Rnext = ']'; - if (_Den != 1) { - _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Den)); - *--_Rnext = '/'; - } +#undef _IF_PERIOD_RETURN_SUFFIX_ELSE - _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Num)); - *--_Rnext = '['; - return _Rnext; + template + _NODISCARD _CharT* _Get_general_unit_suffix(_CharT* _Rnext, const intmax_t _Num, const intmax_t _Den) { + // Returns the head pointer of the string, built in reverse. + _STL_INTERNAL_CHECK(_Num > 0 && _Den > 0); + *--_Rnext = '\0'; + *--_Rnext = 's'; + *--_Rnext = ']'; + if (_Den != 1) { + _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Den)); + *--_Rnext = '/'; } - private: - struct _Suf_names { - const intmax_t _Num; - const intmax_t _Den; - const pair _Name; - }; - - // clang-format off -#define _LITERAL_SUFFIX(_TYPE, _SUFFIX) \ - { _TYPE::num, _TYPE::den, pair{_SUFFIX, L##_SUFFIX} } - // clang-format on - - static constexpr _Suf_names _Suffixes[]{ - _LITERAL_SUFFIX(atto, "as"), - _LITERAL_SUFFIX(femto, "fs"), - _LITERAL_SUFFIX(pico, "ps"), - _LITERAL_SUFFIX(nano, "ns"), - _LITERAL_SUFFIX(micro, "us"), - _LITERAL_SUFFIX(milli, "ms"), - _LITERAL_SUFFIX(centi, "cs"), - _LITERAL_SUFFIX(deci, "ds"), - _LITERAL_SUFFIX(seconds::period, "s"), - _LITERAL_SUFFIX(deca, "das"), - _LITERAL_SUFFIX(hecto, "hs"), - _LITERAL_SUFFIX(kilo, "ks"), - _LITERAL_SUFFIX(mega, "Ms"), - _LITERAL_SUFFIX(giga, "Gs"), - _LITERAL_SUFFIX(tera, "Ts"), - _LITERAL_SUFFIX(peta, "Ps"), - _LITERAL_SUFFIX(exa, "Es"), - _LITERAL_SUFFIX(minutes::period, "min"), - _LITERAL_SUFFIX(hours::period, "h"), - _LITERAL_SUFFIX(ratio<86400>, "d"), - }; - -#undef _LITERAL_SUFFIX - }; + _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Num)); + *--_Rnext = '['; + return _Rnext; + } template basic_ostream<_CharT, _Traits>& operator<<( @@ -697,11 +687,11 @@ namespace chrono { _Sstr.precision(_Os.precision()); _Sstr << _Dur.count(); - constexpr auto _Suffix = _Units_suffix_helper::_Get_literal<_CharT, _Period>(); + constexpr auto _Suffix = _Get_literal_unit_suffix<_CharT, _Period>(); if constexpr (_Suffix == nullptr) { _CharT _Buffer[2 * (numeric_limits::digits10 + 1) + 5] = {}; // 2 numbers + "[/]s\0" const _CharT* const _Begin = - _Units_suffix_helper::_Get_general<_CharT>(_STD end(_Buffer), _Period::num, _Period::den); + _Get_general_unit_suffix<_CharT>(_STD end(_Buffer), _Period::num, _Period::den); _Sstr << _Begin; } else { _Sstr << _Suffix;