From 7124e06e4bc93821c1df3f938b7a606e5a4581b0 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Fri, 14 Jul 2023 21:45:52 +0700 Subject: [PATCH 1/6] Fix iostreams with imbued locales to print INFs correctly --- stl/inc/xlocnum | 8 +++---- tests/std/tests/GH_003867_output_nan/test.cpp | 24 +++++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index a14c070a63e..5eb7205138d 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -1376,7 +1376,7 @@ protected: const auto _Ngen = static_cast(_CSTD sprintf_s( &_Buf[0], _Buf.size(), _Ffmt(_Fmt, 0, _Iosbase.flags()), static_cast(_Precision), _Val)); - return _Fput_v2(_Dest, _Iosbase, _Fill, _Buf.c_str(), _Ngen, (_STD isnan)(_Val)); + return _Fput_v2(_Dest, _Iosbase, _Fill, _Buf.c_str(), _Ngen, (_STD isnan)(_Val) || (_STD isinf)(_Val)); } virtual _OutIt __CLR_OR_THIS_CALL do_put( @@ -1400,7 +1400,7 @@ protected: const auto _Ngen = static_cast(_CSTD sprintf_s( &_Buf[0], _Buf.size(), _Ffmt(_Fmt, 'L', _Iosbase.flags()), static_cast(_Precision), _Val)); - return _Fput_v2(_Dest, _Iosbase, _Fill, _Buf.c_str(), _Ngen, (_STD isnan)(_Val)); + return _Fput_v2(_Dest, _Iosbase, _Fill, _Buf.c_str(), _Ngen, (_STD isnan)(_Val) || (_STD isinf)(_Val)); } #pragma warning(pop) @@ -1468,7 +1468,7 @@ private: template // TRANSITION, ABI _OutIt _Fput_v2(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const char* _Buf, size_t _Count, - bool _Is_nan_val) const { // put formatted floating-point to _Dest + bool _Is_nan_or_inf_val) const { // put formatted floating-point to _Dest auto _Prefix = static_cast(0 < _Count && (*_Buf == '+' || *_Buf == '-')); const char* _Exps; if ((_Iosbase.flags() & ios_base::floatfield) != ios_base::hexfloat) { @@ -1497,7 +1497,7 @@ private: _Groupstring[_Poff] = _Punct_fac.decimal_point(); } - if (!_Is_nan_val) { + if (!_Is_nan_or_inf_val) { size_t _Off = _Poff == _Count ? _Eoff : _Poff; const char* _Pg = &_Grouping[0]; while (*_Pg != CHAR_MAX && '\0' < *_Pg && static_cast(*_Pg) < _Off - _Prefix) { diff --git a/tests/std/tests/GH_003867_output_nan/test.cpp b/tests/std/tests/GH_003867_output_nan/test.cpp index a0c56d286a0..cb2ac74351b 100644 --- a/tests/std/tests/GH_003867_output_nan/test.cpp +++ b/tests/std/tests/GH_003867_output_nan/test.cpp @@ -5,16 +5,32 @@ #include #include #include +#include using namespace std; +struct groups_of_1 : numpunct { + // The standard char specialization of std::numpunct::do_thousands_sep returns ',' + string do_grouping() const { + return "\1"; + } // groups of 1 digit +}; + template void test_gh_3867() { // GH-3867 Writing NaN to the output stream with a set locale results in a weird output - ostringstream s; - s.imbue(locale("en-US")); - s << -numeric_limits::quiet_NaN(); - assert(s.str() == "-nan(ind)"); + { + ostringstream s; + s.imbue(locale("en-US")); + s << -numeric_limits::quiet_NaN(); + assert(s.str() == "-nan(ind)"); + } + { + ostringstream s; + s.imbue(locale(s.getloc(), new groups_of_1)); + s << -numeric_limits::infinity(); + assert(s.str() == "-inf"); + } } int main() { From 9d47803d79ba46b1bf34de61b8bc73c7d9a0ac4c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 14 Jul 2023 12:17:22 -0700 Subject: [PATCH 2/6] `_Fput_v2` => `_Fput_v3`, use `isfinite` --- stl/inc/xlocnum | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 5eb7205138d..1bc6413b0d1 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -1376,7 +1376,7 @@ protected: const auto _Ngen = static_cast(_CSTD sprintf_s( &_Buf[0], _Buf.size(), _Ffmt(_Fmt, 0, _Iosbase.flags()), static_cast(_Precision), _Val)); - return _Fput_v2(_Dest, _Iosbase, _Fill, _Buf.c_str(), _Ngen, (_STD isnan)(_Val) || (_STD isinf)(_Val)); + return _Fput_v3(_Dest, _Iosbase, _Fill, _Buf.c_str(), _Ngen, (_STD isfinite)(_Val)); } virtual _OutIt __CLR_OR_THIS_CALL do_put( @@ -1400,7 +1400,7 @@ protected: const auto _Ngen = static_cast(_CSTD sprintf_s( &_Buf[0], _Buf.size(), _Ffmt(_Fmt, 'L', _Iosbase.flags()), static_cast(_Precision), _Val)); - return _Fput_v2(_Dest, _Iosbase, _Fill, _Buf.c_str(), _Ngen, (_STD isnan)(_Val) || (_STD isinf)(_Val)); + return _Fput_v3(_Dest, _Iosbase, _Fill, _Buf.c_str(), _Ngen, (_STD isfinite)(_Val)); } #pragma warning(pop) @@ -1463,12 +1463,12 @@ private: _OutIt __CLRCALL_OR_CDECL _Fput(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const char* _Buf, size_t _Count) const { // TRANSITION, ABI: preserved for binary compatibility - return _Fput_v2(_Dest, _Iosbase, _Fill, _Buf, _Count, false); + return _Fput_v3(_Dest, _Iosbase, _Fill, _Buf, _Count, true); } template // TRANSITION, ABI - _OutIt _Fput_v2(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const char* _Buf, size_t _Count, - bool _Is_nan_or_inf_val) const { // put formatted floating-point to _Dest + _OutIt _Fput_v3(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const char* _Buf, size_t _Count, + bool _Is_finite_val) const { // put formatted floating-point to _Dest auto _Prefix = static_cast(0 < _Count && (*_Buf == '+' || *_Buf == '-')); const char* _Exps; if ((_Iosbase.flags() & ios_base::floatfield) != ios_base::hexfloat) { @@ -1497,7 +1497,7 @@ private: _Groupstring[_Poff] = _Punct_fac.decimal_point(); } - if (!_Is_nan_or_inf_val) { + if (_Is_finite_val) { size_t _Off = _Poff == _Count ? _Eoff : _Poff; const char* _Pg = &_Grouping[0]; while (*_Pg != CHAR_MAX && '\0' < *_Pg && static_cast(*_Pg) < _Off - _Prefix) { From 9245fcb6fc0404e56375b911570aab2faaa55423 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 14 Jul 2023 12:21:57 -0700 Subject: [PATCH 3/6] Mark `do_grouping()` as `override`. --- tests/std/tests/GH_003867_output_nan/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/GH_003867_output_nan/test.cpp b/tests/std/tests/GH_003867_output_nan/test.cpp index cb2ac74351b..6502ca756eb 100644 --- a/tests/std/tests/GH_003867_output_nan/test.cpp +++ b/tests/std/tests/GH_003867_output_nan/test.cpp @@ -11,7 +11,7 @@ using namespace std; struct groups_of_1 : numpunct { // The standard char specialization of std::numpunct::do_thousands_sep returns ',' - string do_grouping() const { + string do_grouping() const override { return "\1"; } // groups of 1 digit }; From e8ba27936279eb492c48541f7138fa46eeb3bf0b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 14 Jul 2023 12:45:13 -0700 Subject: [PATCH 4/6] Add `TEST_CUSTOM_FACET` guard. --- tests/std/tests/GH_003867_output_nan/test.cpp | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/std/tests/GH_003867_output_nan/test.cpp b/tests/std/tests/GH_003867_output_nan/test.cpp index 6502ca756eb..e36c6779fc8 100644 --- a/tests/std/tests/GH_003867_output_nan/test.cpp +++ b/tests/std/tests/GH_003867_output_nan/test.cpp @@ -9,12 +9,30 @@ using namespace std; +// Because std::string crosses the DLL boundary via overridden virtual functions, +// we can test custom facets only when: +// * linking statically, or +// * linking dynamically with IDL set to its default value (so the user code and the DLL match). +#ifdef _DEBUG +#define DEFAULT_IDL 2 +#else +#define DEFAULT_IDL 0 +#endif + +#if !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL +#define TEST_CUSTOM_FACET 1 +#else +#define TEST_CUSTOM_FACET 0 +#endif + +#if TEST_CUSTOM_FACET struct groups_of_1 : numpunct { // The standard char specialization of std::numpunct::do_thousands_sep returns ',' string do_grouping() const override { return "\1"; } // groups of 1 digit }; +#endif // TEST_CUSTOM_FACET template void test_gh_3867() { @@ -25,12 +43,15 @@ void test_gh_3867() { s << -numeric_limits::quiet_NaN(); assert(s.str() == "-nan(ind)"); } + +#if TEST_CUSTOM_FACET { ostringstream s; s.imbue(locale(s.getloc(), new groups_of_1)); s << -numeric_limits::infinity(); assert(s.str() == "-inf"); } +#endif // TEST_CUSTOM_FACET } int main() { From 798f58f177e0b05af9f29ab532a5d46d03ab298f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 14 Jul 2023 12:49:05 -0700 Subject: [PATCH 5/6] Replace Dev11_0496153_locale_ctor's guard for consistency. --- .../tests/Dev11_0496153_locale_ctor/test.cpp | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/tests/std/tests/Dev11_0496153_locale_ctor/test.cpp b/tests/std/tests/Dev11_0496153_locale_ctor/test.cpp index c7ac08d39eb..73659075e5a 100644 --- a/tests/std/tests/Dev11_0496153_locale_ctor/test.cpp +++ b/tests/std/tests/Dev11_0496153_locale_ctor/test.cpp @@ -10,16 +10,23 @@ using namespace std; -// The following monstrosity avoids activating the test code for _DLL compiles -// that use a non-default value of _ITERATOR_DEBUG_LEVEL (_ITERATOR_DEBUG_LEVEL -// defaults to "0" for release builds and "2" for _DEBUG). -#if defined(_DLL) && (_ITERATOR_DEBUG_LEVEL == 1 || (defined(_DEBUG) != (_ITERATOR_DEBUG_LEVEL == 2))) -#define ENABLE_TEST 0 +// Because std::string crosses the DLL boundary via overridden virtual functions, +// we can test custom facets only when: +// * linking statically, or +// * linking dynamically with IDL set to its default value (so the user code and the DLL match). +#ifdef _DEBUG +#define DEFAULT_IDL 2 #else -#define ENABLE_TEST 1 -#endif // defined(_DLL) && (_ITERATOR_DEBUG_LEVEL == 1 || (defined(_DEBUG) != (_ITERATOR_DEBUG_LEVEL == 2))) +#define DEFAULT_IDL 0 +#endif -#if ENABLE_TEST +#if !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL +#define TEST_CUSTOM_FACET 1 +#else +#define TEST_CUSTOM_FACET 0 +#endif + +#if TEST_CUSTOM_FACET void test_Dev11_496153_locale_ctor_should_not_throw() noexcept { const locale loc(setlocale(LC_ALL, nullptr)); @@ -39,11 +46,11 @@ void test_VSO_159700_locale_should_support_user_defined_facets() { str << 1.5f; assert("locale didn't support user-defined facets" && str.str() == "1,5"); } -#endif // ENABLE_TEST +#endif // TEST_CUSTOM_FACET int main() { -#if ENABLE_TEST +#if TEST_CUSTOM_FACET test_Dev11_496153_locale_ctor_should_not_throw(); test_VSO_159700_locale_should_support_user_defined_facets(); -#endif // ENABLE_TEST +#endif // TEST_CUSTOM_FACET } From f3859c7479cda977ef2a5d9a5dbe499524b3dcdf Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 14 Jul 2023 14:08:51 -0700 Subject: [PATCH 6/6] put_long_double.pass.cpp is now passing! --- tests/libcxx/expected_results.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 224f1ffecac..7b912c8a6b0 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -775,7 +775,6 @@ std/localization/locale.categories/category.monetary/locale.money.put/locale.mon std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp FAIL std/localization/locale.categories/category.monetary/locale.moneypunct/locale.moneypunct.members/decimal_point.pass.cpp FAIL std/localization/locale.categories/category.monetary/locale.moneypunct/locale.moneypunct.members/thousands_sep.pass.cpp FAIL -std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long_double.pass.cpp FAIL std/localization/locale.categories/category.time/locale.time.get/locale.time.get.members/get_monthname_wide.pass.cpp FAIL std/localization/locale.categories/category.time/locale.time.get/locale.time.get.members/get_monthname.pass.cpp FAIL std/localization/locale.categories/category.time/locale.time.get/locale.time.get.members/get_one.pass.cpp FAIL