Skip to content

Commit 6d01edb

Browse files
ZingamH-G-Hristov
authored andcommitted
[libc++][string] P2587R3: to_string or not to_string
1 parent 44f4e43 commit 6d01edb

File tree

9 files changed

+292
-30
lines changed

9 files changed

+292
-30
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ Status
498498
---------------------------------------------------------- -----------------
499499
``__cpp_lib_to_chars`` *unimplemented*
500500
---------------------------------------------------------- -----------------
501-
``__cpp_lib_to_string`` *unimplemented*
501+
``__cpp_lib_to_string`` ``202306L``
502502
---------------------------------------------------------- -----------------
503503
``__cpp_lib_tuple_like`` *unimplemented*
504504
---------------------------------------------------------- -----------------

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"Paper #","Paper Name","Meeting","Status","First released version","Notes"
22
"`P2497R0 <https://wg21.link/P2497R0>`__","Testing for success or failure of ``<charconv>`` functions","2023-06 (Varna)","|Complete|","18",""
33
"`P2592R3 <https://wg21.link/P2592R3>`__","Hashing support for ``std::chrono`` value classes","2023-06 (Varna)","","",""
4-
"`P2587R3 <https://wg21.link/P2587R3>`__","``to_string`` or not ``to_string``","2023-06 (Varna)","","",""
4+
"`P2587R3 <https://wg21.link/P2587R3>`__","``to_string`` or not ``to_string``","2023-06 (Varna)","|Complete|","25",""
55
"`P2562R1 <https://wg21.link/P2562R1>`__","``constexpr`` Stable Sorting","2023-06 (Varna)","|Partial|","20",""
66
"`P2545R4 <https://wg21.link/P2545R4>`__","Read-Copy Update (RCU)","2023-06 (Varna)","","",""
77
"`P2530R3 <https://wg21.link/P2530R3>`__","Hazard Pointers for C++26","2023-06 (Varna)","","",""

libcxx/include/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ __cpp_lib_void_t 201411L <type_traits>
588588
// # define __cpp_lib_text_encoding 202306L
589589
# undef __cpp_lib_to_chars
590590
// # define __cpp_lib_to_chars 202306L
591-
// # define __cpp_lib_to_string 202306L
591+
# define __cpp_lib_to_string 202306L
592592
# undef __cpp_lib_tuple_like
593593
// # define __cpp_lib_tuple_like 202311L
594594
# undef __cpp_lib_variant

libcxx/src/string.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <cerrno>
1111
#include <charconv>
1212
#include <cstdlib>
13+
#include <format>
1314
#include <limits>
1415
#include <stdexcept>
1516
#include <string>
@@ -372,14 +373,30 @@ wstring to_wstring(unsigned long val) { return i_to_string<wstring>(val); }
372373
wstring to_wstring(unsigned long long val) { return i_to_string<wstring>(val); }
373374
#endif
374375

376+
#if _LIBCPP_STD_VER >= 26
377+
378+
string to_string(float val) { return std::format("{}", val); }
379+
string to_string(double val) { return std::format("{}", val); }
380+
string to_string(long double val) { return std::format("{}", val); }
381+
382+
# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
383+
wstring to_wstring(float val) { return std::format(L"{}", val); }
384+
wstring to_wstring(double val) { return std::format(L"{}", val); }
385+
wstring to_wstring(long double val) { return std::format(L"{}", val); }
386+
# endif
387+
388+
#else
389+
375390
string to_string(float val) { return as_string(snprintf, initial_string< string>()(), "%f", val); }
376391
string to_string(double val) { return as_string(snprintf, initial_string< string>()(), "%f", val); }
377392
string to_string(long double val) { return as_string(snprintf, initial_string< string>()(), "%Lf", val); }
378393

379-
#if _LIBCPP_HAS_WIDE_CHARACTERS
394+
# if _LIBCPP_HAS_WIDE_CHARACTERS
380395
wstring to_wstring(float val) { return as_string(get_swprintf(), initial_string<wstring>()(), L"%f", val); }
381396
wstring to_wstring(double val) { return as_string(get_swprintf(), initial_string<wstring>()(), L"%f", val); }
382397
wstring to_wstring(long double val) { return as_string(get_swprintf(), initial_string<wstring>()(), L"%Lf", val); }
383-
#endif
398+
# endif
399+
400+
#endif // _LIBCPP_STD_VER >= 26
384401

385402
_LIBCPP_END_NAMESPACE_STD

libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -488,17 +488,11 @@
488488
# error "__cpp_lib_string_view should have the value 202403L in c++26"
489489
# endif
490490

491-
# if !defined(_LIBCPP_VERSION)
492-
# ifndef __cpp_lib_to_string
493-
# error "__cpp_lib_to_string should be defined in c++26"
494-
# endif
495-
# if __cpp_lib_to_string != 202306L
496-
# error "__cpp_lib_to_string should have the value 202306L in c++26"
497-
# endif
498-
# else // _LIBCPP_VERSION
499-
# ifdef __cpp_lib_to_string
500-
# error "__cpp_lib_to_string should not be defined because it is unimplemented in libc++!"
501-
# endif
491+
# ifndef __cpp_lib_to_string
492+
# error "__cpp_lib_to_string should be defined in c++26"
493+
# endif
494+
# if __cpp_lib_to_string != 202306L
495+
# error "__cpp_lib_to_string should have the value 202306L in c++26"
502496
# endif
503497

504498
#endif // TEST_STD_VER > 23

libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8107,17 +8107,11 @@
81078107
# endif
81088108
# endif
81098109

8110-
# if !defined(_LIBCPP_VERSION)
8111-
# ifndef __cpp_lib_to_string
8112-
# error "__cpp_lib_to_string should be defined in c++26"
8113-
# endif
8114-
# if __cpp_lib_to_string != 202306L
8115-
# error "__cpp_lib_to_string should have the value 202306L in c++26"
8116-
# endif
8117-
# else // _LIBCPP_VERSION
8118-
# ifdef __cpp_lib_to_string
8119-
# error "__cpp_lib_to_string should not be defined because it is unimplemented in libc++!"
8120-
# endif
8110+
# ifndef __cpp_lib_to_string
8111+
# error "__cpp_lib_to_string should be defined in c++26"
8112+
# endif
8113+
# if __cpp_lib_to_string != 202306L
8114+
# error "__cpp_lib_to_string should have the value 202306L in c++26"
81218115
# endif
81228116

81238117
# ifndef __cpp_lib_to_underlying

libcxx/test/std/strings/string.conversions/to_string.pass.cpp

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
// string to_string(double val);
1919
// string to_string(long double val);
2020

21-
#include <string>
2221
#include <cassert>
22+
#include <format>
23+
#include <string>
2324
#include <limits>
2425

2526
#include "parse_integer.h"
@@ -32,29 +33,44 @@ void test_signed() {
3233
assert(s.size() == 1);
3334
assert(s[s.size()] == 0);
3435
assert(s == "0");
36+
#if TEST_STD_VER >= 26
37+
assert(s == std::format("{}", T(0)));
38+
#endif
3539
}
3640
{
3741
std::string s = std::to_string(T(12345));
3842
assert(s.size() == 5);
3943
assert(s[s.size()] == 0);
4044
assert(s == "12345");
45+
#if TEST_STD_VER >= 26
46+
assert(s == std::format("{}", T(12345)));
47+
#endif
4148
}
4249
{
4350
std::string s = std::to_string(T(-12345));
4451
assert(s.size() == 6);
4552
assert(s[s.size()] == 0);
4653
assert(s == "-12345");
54+
#if TEST_STD_VER >= 26
55+
assert(s == std::format("{}", T(-12345)));
56+
#endif
4757
}
4858
{
4959
std::string s = std::to_string(std::numeric_limits<T>::max());
5060
assert(s.size() == std::numeric_limits<T>::digits10 + 1);
5161
T t = parse_integer<T>(s);
5262
assert(t == std::numeric_limits<T>::max());
63+
#if TEST_STD_VER >= 26
64+
assert(s == std::format("{}", std::numeric_limits<T>::max()));
65+
#endif
5366
}
5467
{
5568
std::string s = std::to_string(std::numeric_limits<T>::min());
5669
T t = parse_integer<T>(s);
5770
assert(t == std::numeric_limits<T>::min());
71+
#if TEST_STD_VER >= 26
72+
assert(s == std::format("{}", std::numeric_limits<T>::min()));
73+
#endif
5874
}
5975
}
6076

@@ -65,43 +81,153 @@ void test_unsigned() {
6581
assert(s.size() == 1);
6682
assert(s[s.size()] == 0);
6783
assert(s == "0");
84+
#if TEST_STD_VER >= 26
85+
assert(s == std::format("{}", T(0)));
86+
#endif
6887
}
6988
{
7089
std::string s = std::to_string(T(12345));
7190
assert(s.size() == 5);
7291
assert(s[s.size()] == 0);
7392
assert(s == "12345");
93+
#if TEST_STD_VER >= 26
94+
assert(s == std::format("{}", T(12345)));
95+
#endif
7496
}
7597
{
7698
std::string s = std::to_string(std::numeric_limits<T>::max());
7799
assert(s.size() == std::numeric_limits<T>::digits10 + 1);
78100
T t = parse_integer<T>(s);
79101
assert(t == std::numeric_limits<T>::max());
102+
#if TEST_STD_VER >= 26
103+
assert(s == std::format("{}", std::numeric_limits<T>::max()));
104+
#endif
80105
}
81106
}
82107

83108
template <class T>
84109
void test_float() {
85110
{
86111
std::string s = std::to_string(T(0));
112+
#if TEST_STD_VER < 26
87113
assert(s.size() == 8);
88114
assert(s[s.size()] == 0);
89115
assert(s == "0.000000");
116+
#else
117+
std::string f = std::format("{}", T(0));
118+
assert(s == f);
119+
assert(s == "0");
120+
#endif
90121
}
91122
{
92123
std::string s = std::to_string(T(12345));
124+
#if TEST_STD_VER < 26
93125
assert(s.size() == 12);
94126
assert(s[s.size()] == 0);
95127
assert(s == "12345.000000");
128+
#else
129+
std::string f = std::format("{}", T(12345));
130+
assert(s == f);
131+
assert(s == "12345");
132+
#endif
96133
}
97134
{
98135
std::string s = std::to_string(T(-12345));
136+
#if TEST_STD_VER < 26
99137
assert(s.size() == 13);
100138
assert(s[s.size()] == 0);
101139
assert(s == "-12345.000000");
140+
#else
141+
std::string f = std::format("{}", T(-12345));
142+
assert(s == f);
143+
assert(s == "-12345");
144+
#endif
145+
}
146+
147+
#if TEST_STD_VER >= 26
148+
{
149+
std::string s = std::to_string(T(90.84));
150+
std::string f = std::format("{}", T(90.84));
151+
assert(s == f);
152+
assert(s == "90.84");
102153
}
154+
{
155+
std::string s = std::to_string(T(-90.84));
156+
std::string f = std::format("{}", T(-90.84));
157+
assert(s == f);
158+
assert(s == "-90.84");
159+
}
160+
#endif
161+
}
162+
163+
#if TEST_STD_VER >= 26
164+
165+
template <class T>
166+
void test_float_with_locale(const char* locale, T inputValue, const char* expectedValue) {
167+
setlocale(LC_ALL, locale);
168+
169+
std::string s = std::to_string(inputValue);
170+
std::string f = std::format("{}", inputValue);
171+
assert(s == f);
172+
assert(s == expectedValue);
103173
}
104174

175+
void test_float_with_locale() {
176+
// Locale "C"
177+
178+
test_float_with_locale<float>("C", 0.9084, "0.9084");
179+
test_float_with_locale<double>("C", 0.9084, "0.9084");
180+
test_float_with_locale<long double>("C", 0.9084, "0.9084");
181+
182+
test_float_with_locale<float>("C", -0.9084, "-0.9084");
183+
test_float_with_locale<double>("C", -0.9084, "-0.9084");
184+
test_float_with_locale<long double>("C", -0.9084, "-0.9084");
185+
186+
test_float_with_locale<float>("C", 1e-7, "1e-07");
187+
test_float_with_locale<double>("C", 1e-7, "1e-07");
188+
test_float_with_locale<long double>("C", 1e-7, "1e-07");
189+
190+
test_float_with_locale<float>("C", -1e-7, "-1e-07");
191+
test_float_with_locale<double>("C", -1e-7, "-1e-07");
192+
test_float_with_locale<long double>("C", -1e-7, "-1e-07");
193+
194+
test_float_with_locale<float>("C", 1.7976931348623157e+308, "inf");
195+
test_float_with_locale<double>("C", 1.7976931348623157e+308, "1.7976931348623157e+308");
196+
test_float_with_locale<long double>("C", 1.7976931348623157e+308, "1.7976931348623157e+308");
197+
198+
test_float_with_locale<float>("C", -1.7976931348623157e+308, "-inf");
199+
test_float_with_locale<double>("C", -1.7976931348623157e+308, "-1.7976931348623157e+308");
200+
test_float_with_locale<long double>("C", -1.7976931348623157e+308, "-1.7976931348623157e+308");
201+
202+
// Locale "uk_UA.UTF-8"
203+
204+
test_float_with_locale<float>("uk_UA.UTF-8", 0.9084, "0.9084");
205+
test_float_with_locale<double>("uk_UA.UTF-8", 0.9084, "0.9084");
206+
test_float_with_locale<double>("uk_UA.UTF-8", 0.9084, "0.9084");
207+
208+
test_float_with_locale<float>("uk_UA.UTF-8", -0.9084, "-0.9084");
209+
test_float_with_locale<double>("uk_UA.UTF-8", -0.9084, "-0.9084");
210+
test_float_with_locale<long double>("uk_UA.UTF-8", -0.9084, "-0.9084");
211+
212+
test_float_with_locale<float>("uk_UA.UTF-8", 1e-7, "1e-07");
213+
test_float_with_locale<double>("uk_UA.UTF-8", 1e-7, "1e-07");
214+
test_float_with_locale<long double>("uk_UA.UTF-8", 1e-7, "1e-07");
215+
216+
test_float_with_locale<float>("uk_UA.UTF-8", -1e-7, "-1e-07");
217+
test_float_with_locale<double>("uk_UA.UTF-8", -1e-7, "-1e-07");
218+
test_float_with_locale<long double>("uk_UA.UTF-8", -1e-7, "-1e-07");
219+
220+
test_float_with_locale<float>("uk_UA.UTF-8", 1.7976931348623157e+308, "inf");
221+
test_float_with_locale<double>("uk_UA.UTF-8", 1.7976931348623157e+308, "1.7976931348623157e+308");
222+
test_float_with_locale<long double>("uk_UA.UTF-8", 1.7976931348623157e+308, "1.7976931348623157e+308");
223+
224+
test_float_with_locale<float>("uk_UA.UTF-8", -1.7976931348623157e+308, "-inf");
225+
test_float_with_locale<double>("uk_UA.UTF-8", -1.7976931348623157e+308, "-1.7976931348623157e+308");
226+
test_float_with_locale<long double>("uk_UA.UTF-8", -1.7976931348623157e+308, "-1.7976931348623157e+308");
227+
}
228+
229+
#endif
230+
105231
int main(int, char**) {
106232
test_signed<int>();
107233
test_signed<long>();
@@ -112,6 +238,9 @@ int main(int, char**) {
112238
test_float<float>();
113239
test_float<double>();
114240
test_float<long double>();
241+
#if TEST_STD_VER >= 26
242+
test_float_with_locale();
243+
#endif
115244

116245
return 0;
117246
}

0 commit comments

Comments
 (0)