Skip to content

WIP - [libc++][string] P2587R3: to_string or not to_string #78100

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_to_chars`` *unimplemented*
---------------------------------------------------------- -----------------
``__cpp_lib_to_string`` *unimplemented*
``__cpp_lib_to_string`` ``202306L``
---------------------------------------------------------- -----------------
``__cpp_lib_tuple_like`` *unimplemented*
---------------------------------------------------------- -----------------
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cPapers.csv
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"Paper #","Paper Name","Meeting","Status","First released version","Notes"
"`P2497R0 <https://wg21.link/P2497R0>`__","Testing for success or failure of ``<charconv>`` functions","2023-06 (Varna)","|Complete|","18",""
"`P2592R3 <https://wg21.link/P2592R3>`__","Hashing support for ``std::chrono`` value classes","2023-06 (Varna)","","",""
"`P2587R3 <https://wg21.link/P2587R3>`__","``to_string`` or not ``to_string``","2023-06 (Varna)","","",""
"`P2587R3 <https://wg21.link/P2587R3>`__","``to_string`` or not ``to_string``","2023-06 (Varna)","|Complete|","XX",""
"`P2562R1 <https://wg21.link/P2562R1>`__","``constexpr`` Stable Sorting","2023-06 (Varna)","|Partial|","20",""
"`P2545R4 <https://wg21.link/P2545R4>`__","Read-Copy Update (RCU)","2023-06 (Varna)","","",""
"`P2530R3 <https://wg21.link/P2530R3>`__","Hazard Pointers for C++26","2023-06 (Varna)","","",""
Expand Down
22 changes: 22 additions & 0 deletions libcxx/include/string
Original file line number Diff line number Diff line change
Expand Up @@ -3939,9 +3939,20 @@ _LIBCPP_EXPORTED_FROM_ABI string to_string(long __val);
_LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long __val);
_LIBCPP_EXPORTED_FROM_ABI string to_string(long long __val);
_LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long long __val);

# if _LIBCPP_STD_VER >= 26
_LIBCPP_EXPORTED_FROM_ABI string __to_string(float __val);
_LIBCPP_EXPORTED_FROM_ABI string __to_string(double __val);
_LIBCPP_EXPORTED_FROM_ABI string __to_string(long double __val);

_LIBCPP_HIDE_FROM_ABI string to_string(float __val) { return std::__to_string(__val); }
_LIBCPP_HIDE_FROM_ABI string to_string(double __val) { return std::__to_string(__val); }
_LIBCPP_HIDE_FROM_ABI string to_string(long double __val) { return std::__to_string(__val); }
# else
_LIBCPP_EXPORTED_FROM_ABI string to_string(float __val);
_LIBCPP_EXPORTED_FROM_ABI string to_string(double __val);
_LIBCPP_EXPORTED_FROM_ABI string to_string(long double __val);
# endif // _LIBCPP_STD_VER >= 26

# if _LIBCPP_HAS_WIDE_CHARACTERS
_LIBCPP_EXPORTED_FROM_ABI int stoi(const wstring& __str, size_t* __idx = nullptr, int __base = 10);
Expand All @@ -3960,9 +3971,20 @@ _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long __val);
_LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long __val);
_LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long long __val);
_LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long long __val);

# if _LIBCPP_STD_VER >= 26
_LIBCPP_EXPORTED_FROM_ABI wstring __to_wstring(float __val);
_LIBCPP_EXPORTED_FROM_ABI wstring __to_wstring(double __val);
_LIBCPP_EXPORTED_FROM_ABI wstring __to_wstring(long double __val);

_LIBCPP_HIDE_FROM_ABI wstring to_wstring(float __val) { return std::__to_wstring(__val); }
_LIBCPP_HIDE_FROM_ABI wstring to_wstring(double __val) { return std::__to_wstring(__val); }
_LIBCPP_HIDE_FROM_ABI wstring to_wstring(long double __val) { return std::__to_wstring(__val); }
# else
_LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(float __val);
_LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(double __val);
_LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long double __val);
# endif
# endif // _LIBCPP_HAS_WIDE_CHARACTERS

template <class _CharT, class _Traits, class _Allocator>
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ __cpp_lib_void_t 201411L <type_traits>
// # define __cpp_lib_text_encoding 202306L
# undef __cpp_lib_to_chars
// # define __cpp_lib_to_chars 202306L
// # define __cpp_lib_to_string 202306L
# define __cpp_lib_to_string 202306L
# undef __cpp_lib_tuple_like
// # define __cpp_lib_tuple_like 202311L
# undef __cpp_lib_variant
Expand Down
11 changes: 11 additions & 0 deletions libcxx/src/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <cerrno>
#include <charconv>
#include <cstdlib>
#include <format>
#include <limits>
#include <stdexcept>
#include <string>
Expand Down Expand Up @@ -372,6 +373,16 @@ wstring to_wstring(unsigned long val) { return i_to_string<wstring>(val); }
wstring to_wstring(unsigned long long val) { return i_to_string<wstring>(val); }
#endif

_LIBCPP_EXPORTED_FROM_ABI string __to_string(float __val) { return std::format("{}", __val); }
_LIBCPP_EXPORTED_FROM_ABI string __to_string(double __val) { return std::format("{}", __val); }
_LIBCPP_EXPORTED_FROM_ABI string __to_string(long double __val) { return std::format("{}", __val); }

#if _LIBCPP_HAS_WIDE_CHARACTERS
_LIBCPP_EXPORTED_FROM_ABI wstring __to_wstring(float __val) { return std::format(L"{}", __val); }
_LIBCPP_EXPORTED_FROM_ABI wstring __to_wstring(double __val) { return std::format(L"{}", __val); }
_LIBCPP_EXPORTED_FROM_ABI wstring __to_wstring(long double __val) { return std::format(L"{}", __val); }
#endif

string to_string(float val) { return as_string(snprintf, initial_string< string>()(), "%f", val); }
string to_string(double val) { return as_string(snprintf, initial_string< string>()(), "%f", val); }
string to_string(long double val) { return as_string(snprintf, initial_string< string>()(), "%Lf", val); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,17 +488,11 @@
# error "__cpp_lib_string_view should have the value 202403L in c++26"
# endif

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_to_string
# error "__cpp_lib_to_string should be defined in c++26"
# endif
# if __cpp_lib_to_string != 202306L
# error "__cpp_lib_to_string should have the value 202306L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_to_string
# error "__cpp_lib_to_string should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_to_string
# error "__cpp_lib_to_string should be defined in c++26"
# endif
# if __cpp_lib_to_string != 202306L
# error "__cpp_lib_to_string should have the value 202306L in c++26"
# endif

#endif // TEST_STD_VER > 23
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8107,17 +8107,11 @@
# endif
# endif

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_to_string
# error "__cpp_lib_to_string should be defined in c++26"
# endif
# if __cpp_lib_to_string != 202306L
# error "__cpp_lib_to_string should have the value 202306L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_to_string
# error "__cpp_lib_to_string should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_to_string
# error "__cpp_lib_to_string should be defined in c++26"
# endif
# if __cpp_lib_to_string != 202306L
# error "__cpp_lib_to_string should have the value 202306L in c++26"
# endif

# ifndef __cpp_lib_to_underlying
Expand Down
132 changes: 131 additions & 1 deletion libcxx/test/std/strings/string.conversions/to_string.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
// string to_string(double val);
// string to_string(long double val);

#include <string>
#include <cassert>
#include <clocale>
#include <format>
#include <string>
#include <limits>

#include "parse_integer.h"
Expand All @@ -32,29 +34,44 @@ void test_signed() {
assert(s.size() == 1);
assert(s[s.size()] == 0);
assert(s == "0");
#if TEST_STD_VER >= 26
assert(s == std::format("{}", T(0)));
#endif
}
{
std::string s = std::to_string(T(12345));
assert(s.size() == 5);
assert(s[s.size()] == 0);
assert(s == "12345");
#if TEST_STD_VER >= 26
assert(s == std::format("{}", T(12345)));
#endif
}
{
std::string s = std::to_string(T(-12345));
assert(s.size() == 6);
assert(s[s.size()] == 0);
assert(s == "-12345");
#if TEST_STD_VER >= 26
assert(s == std::format("{}", T(-12345)));
#endif
}
{
std::string s = std::to_string(std::numeric_limits<T>::max());
assert(s.size() == std::numeric_limits<T>::digits10 + 1);
T t = parse_integer<T>(s);
assert(t == std::numeric_limits<T>::max());
#if TEST_STD_VER >= 26
assert(s == std::format("{}", std::numeric_limits<T>::max()));
#endif
}
{
std::string s = std::to_string(std::numeric_limits<T>::min());
T t = parse_integer<T>(s);
assert(t == std::numeric_limits<T>::min());
#if TEST_STD_VER >= 26
assert(s == std::format("{}", std::numeric_limits<T>::min()));
#endif
}
}

Expand All @@ -65,43 +82,153 @@ void test_unsigned() {
assert(s.size() == 1);
assert(s[s.size()] == 0);
assert(s == "0");
#if TEST_STD_VER >= 26
assert(s == std::format("{}", T(0)));
#endif
}
{
std::string s = std::to_string(T(12345));
assert(s.size() == 5);
assert(s[s.size()] == 0);
assert(s == "12345");
#if TEST_STD_VER >= 26
assert(s == std::format("{}", T(12345)));
#endif
}
{
std::string s = std::to_string(std::numeric_limits<T>::max());
assert(s.size() == std::numeric_limits<T>::digits10 + 1);
T t = parse_integer<T>(s);
assert(t == std::numeric_limits<T>::max());
#if TEST_STD_VER >= 26
assert(s == std::format("{}", std::numeric_limits<T>::max()));
#endif
}
}

template <class T>
void test_float() {
{
std::string s = std::to_string(T(0));
#if TEST_STD_VER < 26
assert(s.size() == 8);
assert(s[s.size()] == 0);
assert(s == "0.000000");
#else
std::string f = std::format("{}", T(0));
assert(s == f);
assert(s == "0");
#endif
}
{
std::string s = std::to_string(T(12345));
#if TEST_STD_VER < 26
assert(s.size() == 12);
assert(s[s.size()] == 0);
assert(s == "12345.000000");
#else
std::string f = std::format("{}", T(12345));
assert(s == f);
assert(s == "12345");
#endif
}
{
std::string s = std::to_string(T(-12345));
#if TEST_STD_VER < 26
assert(s.size() == 13);
assert(s[s.size()] == 0);
assert(s == "-12345.000000");
#else
std::string f = std::format("{}", T(-12345));
assert(s == f);
assert(s == "-12345");
#endif
}

#if TEST_STD_VER >= 26
{
std::string s = std::to_string(T(90.84));
std::string f = std::format("{}", T(90.84));
assert(s == f);
assert(s == "90.84");
}
{
std::string s = std::to_string(T(-90.84));
std::string f = std::format("{}", T(-90.84));
assert(s == f);
assert(s == "-90.84");
}
#endif
}

#if TEST_STD_VER >= 26

template <class T>
void test_float_with_locale(const char* locale, T inputValue, const char* expectedValue) {
setlocale(LC_ALL, locale);

std::string s = std::to_string(inputValue);
std::string f = std::format("{}", inputValue);
assert(s == f);
assert(s == expectedValue);
}

void test_float_with_locale() {
// Locale "C"

test_float_with_locale<float>("C", 0.9084, "0.9084");
test_float_with_locale<double>("C", 0.9084, "0.9084");
test_float_with_locale<long double>("C", 0.9084, "0.9084");

test_float_with_locale<float>("C", -0.9084, "-0.9084");
test_float_with_locale<double>("C", -0.9084, "-0.9084");
test_float_with_locale<long double>("C", -0.9084, "-0.9084");

test_float_with_locale<float>("C", 1e-7, "1e-07");
test_float_with_locale<double>("C", 1e-7, "1e-07");
test_float_with_locale<long double>("C", 1e-7, "1e-07");

test_float_with_locale<float>("C", -1e-7, "-1e-07");
test_float_with_locale<double>("C", -1e-7, "-1e-07");
test_float_with_locale<long double>("C", -1e-7, "-1e-07");

test_float_with_locale<float>("C", 1.7976931348623157e+308, "inf");
test_float_with_locale<double>("C", 1.7976931348623157e+308, "1.7976931348623157e+308");
test_float_with_locale<long double>("C", 1.7976931348623157e+308, "1.7976931348623157e+308");

test_float_with_locale<float>("C", -1.7976931348623157e+308, "-inf");
test_float_with_locale<double>("C", -1.7976931348623157e+308, "-1.7976931348623157e+308");
test_float_with_locale<long double>("C", -1.7976931348623157e+308, "-1.7976931348623157e+308");

// Locale "uk_UA.UTF-8"

test_float_with_locale<float>("uk_UA.UTF-8", 0.9084, "0.9084");
test_float_with_locale<double>("uk_UA.UTF-8", 0.9084, "0.9084");
test_float_with_locale<double>("uk_UA.UTF-8", 0.9084, "0.9084");

test_float_with_locale<float>("uk_UA.UTF-8", -0.9084, "-0.9084");
test_float_with_locale<double>("uk_UA.UTF-8", -0.9084, "-0.9084");
test_float_with_locale<long double>("uk_UA.UTF-8", -0.9084, "-0.9084");

test_float_with_locale<float>("uk_UA.UTF-8", 1e-7, "1e-07");
test_float_with_locale<double>("uk_UA.UTF-8", 1e-7, "1e-07");
test_float_with_locale<long double>("uk_UA.UTF-8", 1e-7, "1e-07");

test_float_with_locale<float>("uk_UA.UTF-8", -1e-7, "-1e-07");
test_float_with_locale<double>("uk_UA.UTF-8", -1e-7, "-1e-07");
test_float_with_locale<long double>("uk_UA.UTF-8", -1e-7, "-1e-07");

test_float_with_locale<float>("uk_UA.UTF-8", 1.7976931348623157e+308, "inf");
test_float_with_locale<double>("uk_UA.UTF-8", 1.7976931348623157e+308, "1.7976931348623157e+308");
test_float_with_locale<long double>("uk_UA.UTF-8", 1.7976931348623157e+308, "1.7976931348623157e+308");

test_float_with_locale<float>("uk_UA.UTF-8", -1.7976931348623157e+308, "-inf");
test_float_with_locale<double>("uk_UA.UTF-8", -1.7976931348623157e+308, "-1.7976931348623157e+308");
test_float_with_locale<long double>("uk_UA.UTF-8", -1.7976931348623157e+308, "-1.7976931348623157e+308");
}

#endif

int main(int, char**) {
test_signed<int>();
test_signed<long>();
Expand All @@ -112,6 +239,9 @@ int main(int, char**) {
test_float<float>();
test_float<double>();
test_float<long double>();
#if TEST_STD_VER >= 26
test_float_with_locale();
#endif

return 0;
}
Loading
Loading