Skip to content
Draft
Show file tree
Hide file tree
Changes from 8 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