diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst index abf1570737df9..74b261eda6935 100644 --- a/libcxx/docs/ReleaseNotes/19.rst +++ b/libcxx/docs/ReleaseNotes/19.rst @@ -44,6 +44,7 @@ Implemented Papers - P2495R3 - Interfacing ``stringstream``\s with ``string_view`` - P2867R2 - Remove Deprecated ``strstream``\s From C++26 - P2872R3 - Remove ``wstring_convert`` From C++26 +- P3142R0 - Printing Blank Lines with ``println`` (as DR against C++23) - P2302R4 - ``std::ranges::contains`` - P1659R3 - ``std::ranges::starts_with`` and ``std::ranges::ends_with`` diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index af270150fda0c..fa11da62bc080 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -51,7 +51,7 @@ "`P2869R4 `__","LWG","Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26","Tokyo March 2024","","","" "`P2872R3 `__","LWG","Remove ``wstring_convert`` From C++26","Tokyo March 2024","|Complete|","19.0","" "`P3107R5 `__","LWG","Permit an efficient implementation of ``std::print``","Tokyo March 2024","","","|format| |DR|" -"`P3142R0 `__","LWG","Printing Blank Lines with ``println``","Tokyo March 2024","","","|format|" +"`P3142R0 `__","LWG","Printing Blank Lines with ``println``","Tokyo March 2024","|Complete|","19.0","|format|" "`P2845R8 `__","LWG","Formatting of ``std::filesystem::path``","Tokyo March 2024","","","|format|" "`P0493R5 `__","LWG","Atomic minimum/maximum","Tokyo March 2024","","","" "`P2542R8 `__","LWG","``views::concat``","Tokyo March 2024","","","|ranges|" diff --git a/libcxx/include/ostream b/libcxx/include/ostream index 42819ceb252c6..d4fc1c58b8a94 100644 --- a/libcxx/include/ostream +++ b/libcxx/include/ostream @@ -164,6 +164,7 @@ template void print(ostream& os, format_string fmt, Args&&... args); template // since C++23 void println(ostream& os, format_string fmt, Args&&... args); +void println(ostream& os); // since C++26 void vprint_unicode(ostream& os, string_view fmt, format_args args); // since C++23 void vprint_nonunicode(ostream& os, string_view fmt, format_args args); // since C++23 @@ -1163,6 +1164,9 @@ _LIBCPP_HIDE_FROM_ABI void println(ostream& __os, format_string<_Args...> __fmt, # endif // _LIBCPP_HAS_NO_UNICODE } +template // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563). +_LIBCPP_HIDE_FROM_ABI inline void println(ostream& __os) { std::print(__os, "\n"); } + #endif // _LIBCPP_STD_VER >= 23 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/print b/libcxx/include/print index a9f10433a7dc6..e0bcf214ea239 100644 --- a/libcxx/include/print +++ b/libcxx/include/print @@ -15,8 +15,10 @@ namespace std { // [print.fun], print functions template void print(format_string fmt, Args&&... args); + void println(); // Since C++26 template void print(FILE* stream, format_string fmt, Args&&... args); + void println(FILE* stream); // Since C++26 template void println(format_string fmt, Args&&... args); @@ -356,6 +358,12 @@ _LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt # endif // _LIBCPP_HAS_NO_UNICODE } +template // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563). +_LIBCPP_HIDE_FROM_ABI inline void println(FILE* __stream) { std::print(__stream, "\n"); } + +template // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563). +_LIBCPP_HIDE_FROM_ABI inline void println() { println(stdout); } + template _LIBCPP_HIDE_FROM_ABI void println(format_string<_Args...> __fmt, _Args&&... __args) { std::println(stdout, __fmt, std::forward<_Args>(__args)...); diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp index 6b62e2f1754de..2e19e38e2ed04 100644 --- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp +++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp @@ -26,6 +26,7 @@ // void print(ostream& os, format_string fmt, Args&&... args); // template // void println(ostream& os, format_string fmt, Args&&... args); +// void println(ostream& os); // since C++26 // // void vprint_unicode(ostream& os, string_view fmt, format_args args); // void vprint_nonunicode(ostream& os, string_view fmt, format_args args); @@ -67,7 +68,7 @@ test(std::stringstream& stream, std::string expected, test_format_string(args)...); std::string out = stream.str(); TEST_REQUIRE(out == expected, @@ -111,6 +112,7 @@ static void test(std::string expected, std::locale loc, test_format_string { string_type do_truename() const override { return "gültig"; } string_type do_falsename() const override { return "ungültig"; } @@ -2188,12 +2190,47 @@ static void test_floating_point() { test_floating_point_default_precision(); } +static void test_println_blank_line(std::stringstream& stream) { + std::string expected{'\n'}; + stream.str(""); + + std::println(stream); + std::string out = stream.str(); + TEST_REQUIRE(out == expected, + TEST_WRITE_CONCATENATED("\nExpected output (blank line) ", expected, "\nActual output ", out, '\n')); +} + +static void test_println_blank_line(std::locale loc) { + std::stringstream stream; + stream.imbue(loc); + test_println_blank_line(stream); +} + +static void test_println_blank_line() { + std::locale::global(std::locale(LOCALE_en_US_UTF_8)); + assert(std::locale().name() == LOCALE_en_US_UTF_8); + std::stringstream stream; + test_println_blank_line(stream); + + std::locale loc = std::locale(std::locale(), new numpunct()); + std::locale::global(loc); + test_println_blank_line(std::locale(LOCALE_en_US_UTF_8)); + +#ifndef TEST_HAS_NO_UNICODE + + std::locale loc_unicode = std::locale(std::locale(), new numpunct_unicode()); + test_println_blank_line(loc_unicode); + +#endif // TEST_HAS_NO_UNICODE +} + int main(int, char**) { test_bool(); test_integer(); test_floating_point(); test_floating_point(); test_floating_point(); + test_println_blank_line(); return 0; } diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/println.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/println.pass.cpp index 479a3de0a93c8..19a02638a9da1 100644 --- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/println.pass.cpp +++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/println.pass.cpp @@ -17,6 +17,7 @@ // template // void println(ostream& os, format_string fmt, Args&&... args); +// void println(ostream& os); // since C++26 // [ostream.formatted.print]/3 // If the function is vprint_unicode and os is a stream that refers to @@ -55,8 +56,20 @@ auto test_exception = [](std::string_view, std::string_view, Args // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument. }; +void test_println_blank_line() { + std::string expected{'\n'}; + + std::stringstream sstr; + std::println(sstr); + + std::string out = sstr.str(); + TEST_REQUIRE(out == expected, + TEST_WRITE_CONCATENATED("\nExpected output (blank line) ", expected, "\nActual output ", out, '\n')); +}; + int main(int, char**) { print_tests(test_file, test_exception); + test_println_blank_line(); return 0; } diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp index f502616b677b7..ffa48c5e745d6 100644 --- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp +++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp @@ -25,8 +25,10 @@ // template // void print(FILE* stream, format_string fmt, Args&&... args); +// void println(); // Since C++26 // template // void println(FILE* stream, format_string fmt, Args&&... args); +// void println(FILE* stream); // Since C++26 // void vprint_unicode(FILE* stream, string_view fmt, format_args args); // void vprint_nonunicode(FILE* stream, string_view fmt, format_args args); @@ -63,6 +65,20 @@ static void test_println() { assert(std::string_view(buffer.data(), pos) == "hello world!\n"); } +static void test_println_blank_line() { + std::array buffer{0}; + + FILE* file = fmemopen(buffer.data(), buffer.size(), "wb"); + assert(file); + + std::println(file); + long pos = std::ftell(file); + std::fclose(file); + + assert(pos > 0); + assert(std::string_view(buffer.data(), pos) == "\n"); +} + static void test_vprint_unicode() { std::array buffer{0}; @@ -96,6 +112,7 @@ static void test_vprint_nonunicode() { int main(int, char**) { test_print(); test_println(); + test_println_blank_line(); test_vprint_unicode(); test_vprint_nonunicode(); diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/println.blank_line.sh.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/println.blank_line.sh.cpp new file mode 100644 index 0000000000000..93c3563d501b2 --- /dev/null +++ b/libcxx/test/std/input.output/iostream.format/print.fun/println.blank_line.sh.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: no-filesystem +// UNSUPPORTED: executor-has-no-bash +// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME + +// FIXME PRINT How to test println on Windows? +// XFAIL: msvc, target={{.+}}-windows-gnu + +// XFAIL: availability-fp_to_chars-missing + +// + +// void println(); + +// Testing this properly is quite hard; the function unconditionally +// writes to stdout. When stdout is redirected to a file it is no longer +// considered a terminal. The function is a small wrapper around +// +// template +// void println(FILE* stream, format_string fmt, Args&&... args); +// +// So do minimal tests for this function and rely on the FILE* overload +// to do more testing. +// +// The testing is based on the testing for std::cout. + +// TODO PRINT Use lit builtin echo + +// FILE_DEPENDENCIES: echo.sh +// RUN: %{build} +// RUN: %{exec} bash echo.sh -ne "\n" > %t.expected +// RUN: %{exec} "%t.exe" > %t.actual +// RUN: diff -u %t.actual %t.expected + +#include + +int main(int, char**) { + std::println(); + + return 0; +} diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/println.file.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/println.file.pass.cpp index 07272ebb57e5f..2f088e7a7db5f 100644 --- a/libcxx/test/std/input.output/iostream.format/print.fun/println.file.pass.cpp +++ b/libcxx/test/std/input.output/iostream.format/print.fun/println.file.pass.cpp @@ -129,6 +129,29 @@ static void test_new_line() { } } +static void test_println_blank_line() { + // Text does newline translation. + { + FILE* file = fopen(filename.c_str(), "w"); + assert(file); + + std::println(file); +#ifndef _WIN32 + assert(std::ftell(file) == 1); +#else + assert(std::ftell(file) == 2); +#endif + } + // Binary no newline translation. + { + FILE* file = fopen(filename.c_str(), "wb"); + assert(file); + + std::println(file); + assert(std::ftell(file) == 1); + } +} + int main(int, char**) { print_tests(test_file, test_exception); @@ -137,6 +160,7 @@ int main(int, char**) { #endif test_read_only(); test_new_line(); + test_println_blank_line(); return 0; }