[libcxx-commits] [libcxx] [libc++][format] P3142R0: Printing Blank Lines with `println` (PR #87277)

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Fri Apr 5 13:12:12 PDT 2024


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/87277

>From 327f8fe8ab2bda7e7f46ba5b2cbfd2a37c51b1cc Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 29 Mar 2024 18:13:32 +0200
Subject: [PATCH 1/9] [libc++][format] P3142R0 - Printing Blank Lines with
 `println`

Implements https://wg21.link/P3142R0
---
 libcxx/docs/ReleaseNotes/19.rst                |  1 +
 libcxx/include/ostream                         |  3 +++
 libcxx/include/print                           |  6 ++++++
 .../locale-specific_form.pass.cpp              |  4 +++-
 .../ostream.formatted.print/println.pass.cpp   |  4 +++-
 .../print.fun/no_file_description.pass.cpp     |  5 ++++-
 .../print.fun/println.file.pass.cpp            | 18 ++++++++++++++++++
 .../iostream.format/print.fun/println.sh.cpp   |  3 ++-
 8 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index dd39c1bbbc78a3..7bf1da91e2abb9 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -42,6 +42,7 @@ Implemented Papers
 - P2652R2 - Disallow User Specialization of ``allocator_traits``
 - P2819R2 - Add ``tuple`` protocol to ``complex``
 - P2495R3 - Interfacing ``stringstream``\s with ``string_view``
+- 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/include/ostream b/libcxx/include/ostream
index 42819ceb252c65..0c97f48da5c5a4 100644
--- a/libcxx/include/ostream
+++ b/libcxx/include/ostream
@@ -164,6 +164,7 @@ template<class... Args>
   void print(ostream& os, format_string<Args...> fmt, Args&&... args);
 template<class... Args>                                                                                // since C++23
   void println(ostream& os, format_string<Args...> 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,8 @@ _LIBCPP_HIDE_FROM_ABI void println(ostream& __os, format_string<_Args...> __fmt,
 #  endif // _LIBCPP_HAS_NO_UNICODE
 }
 
+_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 a9f10433a7dc61..e12dfe999c64d7 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -15,8 +15,10 @@ namespace std {
   // [print.fun], print functions
   template<class... Args>
     void print(format_string<Args...> fmt, Args&&... args);
+  void println();                                                          // Since C++26
   template<class... Args>
     void print(FILE* stream, format_string<Args...> fmt, Args&&... args);
+  void println(FILE* stream);                                              // Since C++26
 
   template<class... Args>
     void println(format_string<Args...> fmt, Args&&... args);
@@ -356,6 +358,10 @@ _LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt
 #  endif // _LIBCPP_HAS_NO_UNICODE
 }
 
+_LIBCPP_HIDE_FROM_ABI inline void println(FILE* __stream) { std::print(__stream, "\n"); }
+
+_LIBCPP_HIDE_FROM_ABI inline void println() { println(stdout); }
+
 template <class... _Args>
 _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 6b62e2f1754de5..8edfb90b15b86d 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<Args...> fmt, Args&&... args);
 // template<class... Args>
 //   void println(ostream& os, format_string<Args...> 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);
@@ -86,10 +87,11 @@ test(std::stringstream& stream, std::string expected, test_format_string<char, A
   }
   // *** println ***
   {
-    expected += '\n'; // Tested last since it changes the expected value.
+    expected += "\n\n"; // Tested last since it changes the expected value.
     stream.str("");
     ;
     std::println(stream, fmt, std::forward<Args>(args)...);
+    std::println(stream);
     std::string out = stream.str();
     TEST_REQUIRE(out == expected,
                  TEST_WRITE_CONCATENATED(
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 479a3de0a93c8d..24b1062775493a 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<class... Args>
 //   void println(ostream& os, format_string<Args...> 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
@@ -37,9 +38,10 @@
 #include "test_macros.h"
 
 auto test_file = []<class... Args>(std::string_view e, test_format_string<char, Args...> fmt, Args&&... args) {
-  std::string expected = std::string{e} + '\n';
+  std::string expected = '\n' + std::string{e} + '\n';
 
   std::stringstream sstr;
+  std::println(sstr);
   std::println(sstr, fmt, std::forward<Args>(args)...);
 
   std::string out = sstr.str();
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 f502616b677b77..5144e61afaec59 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<class... Args>
 //   void print(FILE* stream, format_string<Args...> fmt, Args&&... args);
+// void println();                                                          // Since C++26
 // template<class... Args>
 //   void println(FILE* stream, format_string<Args...> 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);
 
@@ -55,12 +57,13 @@ static void test_println() {
   FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
   assert(file);
 
+  std::println(file);
   std::println(file, "hello world{}", '!');
   long pos = std::ftell(file);
   std::fclose(file);
 
   assert(pos > 0);
-  assert(std::string_view(buffer.data(), pos) == "hello world!\n");
+  assert(std::string_view(buffer.data(), pos) == "\nhello world!\n");
 }
 
 static void test_vprint_unicode() {
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 07272ebb57e5fc..9e96864aa822dd 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
@@ -112,6 +112,17 @@ static void test_new_line() {
     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
+  }
+  {
+    FILE* file = fopen(filename.c_str(), "w");
+    assert(file);
+
     std::println(file, "");
 #ifndef _WIN32
     assert(std::ftell(file) == 1);
@@ -124,6 +135,13 @@ static void test_new_line() {
     FILE* file = fopen(filename.c_str(), "wb");
     assert(file);
 
+    std::println(file);
+    assert(std::ftell(file) == 1);
+  }
+  {
+    FILE* file = fopen(filename.c_str(), "wb");
+    assert(file);
+
     std::println(file, "");
     assert(std::ftell(file) == 1);
   }
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/println.sh.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/println.sh.cpp
index b811b4f2a24933..b93fa723ee2174 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/println.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/println.sh.cpp
@@ -36,7 +36,7 @@
 
 // FILE_DEPENDENCIES: echo.sh
 // RUN: %{build}
-// RUN: %{exec} bash echo.sh -ne "1234 一二三四\ntrue 0x0\n" > %t.expected
+// RUN: %{exec} bash echo.sh -ne "\n1234 一二三四\ntrue 0x0\n" > %t.expected
 // RUN: %{exec} "%t.exe" > %t.actual
 // RUN: diff -u %t.actual %t.expected
 
@@ -44,6 +44,7 @@
 
 int main(int, char**) {
   // The data is passed as-is so it does not depend on the encoding of the input.
+  std::println();
   std::println("{} {}", 1234, "一二三四");
   std::println("{} {}", true, nullptr);
 

>From d39dbbedf29b09a35787841041ca43a8460ed4bf Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 1 Apr 2024 23:20:52 +0300
Subject: [PATCH 2/9] Try to fix Apple CI

---
 libcxx/include/ostream | 1 +
 libcxx/include/print   | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/libcxx/include/ostream b/libcxx/include/ostream
index 0c97f48da5c5a4..d4fc1c58b8a941 100644
--- a/libcxx/include/ostream
+++ b/libcxx/include/ostream
@@ -1164,6 +1164,7 @@ _LIBCPP_HIDE_FROM_ABI void println(ostream& __os, format_string<_Args...> __fmt,
 #  endif // _LIBCPP_HAS_NO_UNICODE
 }
 
+template <class = void> // 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
diff --git a/libcxx/include/print b/libcxx/include/print
index e12dfe999c64d7..e0bcf214ea239b 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -358,8 +358,10 @@ _LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt
 #  endif // _LIBCPP_HAS_NO_UNICODE
 }
 
+template <class = void> // 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 <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void println() { println(stdout); }
 
 template <class... _Args>

>From 36c65f2c06c9d2350bd7104777d4a6a1f86ce056 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 1 Apr 2024 23:24:26 +0300
Subject: [PATCH 3/9] Updated tests for consistency

---
 .../ostream.formatted.print/println.pass.cpp                  | 4 ++--
 .../iostream.format/print.fun/no_file_description.pass.cpp    | 4 ++--
 .../std/input.output/iostream.format/print.fun/println.sh.cpp | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

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 24b1062775493a..b61fcf174422af 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
@@ -38,11 +38,11 @@
 #include "test_macros.h"
 
 auto test_file = []<class... Args>(std::string_view e, test_format_string<char, Args...> fmt, Args&&... args) {
-  std::string expected = '\n' + std::string{e} + '\n';
+  std::string expected = std::string{e} + "\n\n";
 
   std::stringstream sstr;
-  std::println(sstr);
   std::println(sstr, fmt, std::forward<Args>(args)...);
+  std::println(sstr);
 
   std::string out = sstr.str();
   TEST_REQUIRE(out == expected,
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 5144e61afaec59..450ca45a6d4b7e 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
@@ -57,13 +57,13 @@ static void test_println() {
   FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
   assert(file);
 
-  std::println(file);
   std::println(file, "hello world{}", '!');
+  std::println(file);
   long pos = std::ftell(file);
   std::fclose(file);
 
   assert(pos > 0);
-  assert(std::string_view(buffer.data(), pos) == "\nhello world!\n");
+  assert(std::string_view(buffer.data(), pos) == "hello world!\n\n");
 }
 
 static void test_vprint_unicode() {
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/println.sh.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/println.sh.cpp
index b93fa723ee2174..67343b3a730c54 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/println.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/println.sh.cpp
@@ -36,7 +36,7 @@
 
 // FILE_DEPENDENCIES: echo.sh
 // RUN: %{build}
-// RUN: %{exec} bash echo.sh -ne "\n1234 一二三四\ntrue 0x0\n" > %t.expected
+// RUN: %{exec} bash echo.sh -ne "1234 一二三四\ntrue 0x0\n\n" > %t.expected
 // RUN: %{exec} "%t.exe" > %t.actual
 // RUN: diff -u %t.actual %t.expected
 
@@ -44,9 +44,9 @@
 
 int main(int, char**) {
   // The data is passed as-is so it does not depend on the encoding of the input.
-  std::println();
   std::println("{} {}", 1234, "一二三四");
   std::println("{} {}", true, nullptr);
+  std::println();
 
   return 0;
 }

>From 8c93e195cc58a256b0929971f8518cf63938cc6e Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 4 Apr 2024 23:19:38 +0300
Subject: [PATCH 4/9] Addressed review comments

---
 libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index a34dad58168078..e0e54d75a8850b 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -51,7 +51,7 @@
 "`P2869R4 <https://wg21.link/P2869R4>`__","LWG","Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26","Tokyo March 2024","","",""
 "`P2872R3 <https://wg21.link/P2872R3>`__","LWG","Remove ``wstring_convert`` >From C++26","Tokyo March 2024","","",""
 "`P3107R5 <https://wg21.link/P3107R5>`__","LWG","Permit an efficient implementation of ``std::print``","Tokyo March 2024","","","|format| |DR|"
-"`P3142R0 <https://wg21.link/P3142R0>`__","LWG","Printing Blank Lines with ``println``","Tokyo March 2024","","","|format|"
+"`P3142R0 <https://wg21.link/P3142R0>`__","LWG","Printing Blank Lines with ``println``","Tokyo March 2024","|Complete|","19.0","|format|"
 "`P2845R8 <https://wg21.link/P2845R8>`__","LWG","Formatting of ``std::filesystem::path``","Tokyo March 2024","","","|format|"
 "`P0493R5 <https://wg21.link/P0493R5>`__","LWG","Atomic minimum/maximum","Tokyo March 2024","","",""
 "`P2542R8 <https://wg21.link/P2542R8>`__","LWG","``views::concat``","Tokyo March 2024","","","|ranges|"

>From 02fbb0b66af6c14ce5ff6ffd0d45128dd00dabf6 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 5 Apr 2024 00:07:37 +0300
Subject: [PATCH 5/9] Split tests 1

---
 .../locale-specific_form.pass.cpp             |  3 +--
 .../ostream.formatted.print/println.pass.cpp  |  3 +--
 .../print.fun/no_file_description.pass.cpp    | 16 ++++++++++++-
 .../print.fun/println.file.pass.cpp           | 24 ++++++++++++-------
 4 files changed, 32 insertions(+), 14 deletions(-)

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 8edfb90b15b86d..231c790eee5d26 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
@@ -87,11 +87,10 @@ test(std::stringstream& stream, std::string expected, test_format_string<char, A
   }
   // *** println ***
   {
-    expected += "\n\n"; // Tested last since it changes the expected value.
+    expected += '\n'; // Tested last since it changes the expected value.
     stream.str("");
     ;
     std::println(stream, fmt, std::forward<Args>(args)...);
-    std::println(stream);
     std::string out = stream.str();
     TEST_REQUIRE(out == expected,
                  TEST_WRITE_CONCATENATED(
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 b61fcf174422af..a752323c2aa039 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
@@ -38,11 +38,10 @@
 #include "test_macros.h"
 
 auto test_file = []<class... Args>(std::string_view e, test_format_string<char, Args...> fmt, Args&&... args) {
-  std::string expected = std::string{e} + "\n\n";
+  std::string expected = std::string{e} + '\n';
 
   std::stringstream sstr;
   std::println(sstr, fmt, std::forward<Args>(args)...);
-  std::println(sstr);
 
   std::string out = sstr.str();
   TEST_REQUIRE(out == expected,
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 450ca45a6d4b7e..ffa48c5e745d65 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
@@ -58,12 +58,25 @@ static void test_println() {
   assert(file);
 
   std::println(file, "hello world{}", '!');
+  long pos = std::ftell(file);
+  std::fclose(file);
+
+  assert(pos > 0);
+  assert(std::string_view(buffer.data(), pos) == "hello world!\n");
+}
+
+static void test_println_blank_line() {
+  std::array<char, 100> 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) == "hello world!\n\n");
+  assert(std::string_view(buffer.data(), pos) == "\n");
 }
 
 static void test_vprint_unicode() {
@@ -99,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.file.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/println.file.pass.cpp
index 9e96864aa822dd..2f088e7a7db5f2 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
@@ -112,37 +112,42 @@ static void test_new_line() {
     FILE* file = fopen(filename.c_str(), "w");
     assert(file);
 
-    std::println(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(), "w");
+    FILE* file = fopen(filename.c_str(), "wb");
     assert(file);
 
     std::println(file, "");
-#ifndef _WIN32
     assert(std::ftell(file) == 1);
-#else
-    assert(std::ftell(file) == 2);
-#endif
   }
-  // Binary no newline translation.
+}
+
+static void test_println_blank_line() {
+  // Text does newline translation.
   {
-    FILE* file = fopen(filename.c_str(), "wb");
+    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, "");
+    std::println(file);
     assert(std::ftell(file) == 1);
   }
 }
@@ -155,6 +160,7 @@ int main(int, char**) {
 #endif
   test_read_only();
   test_new_line();
+  test_println_blank_line();
 
   return 0;
 }

>From 6de4539ae0b97411aeca7a98ed88a7961a4c9bda Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 5 Apr 2024 00:30:48 +0300
Subject: [PATCH 6/9] Split tests 2

---
 .../locale-specific_form.pass.cpp                    | 10 ++++++++++
 .../ostream.formatted.print/println.pass.cpp         | 12 ++++++++++++
 2 files changed, 22 insertions(+)

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 231c790eee5d26..b6c755a7a5776a 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
@@ -96,6 +96,16 @@ test(std::stringstream& stream, std::string expected, test_format_string<char, A
                  TEST_WRITE_CONCATENATED(
                      "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
   }
+  // *** println blank line***
+  {
+    expected = "\n";
+    stream.str("");
+    ;
+    std::println(stream);
+    std::string out = stream.str();
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED("\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
 }
 
 template <class... Args>
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 a752323c2aa039..17aedc076cf2d0 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
@@ -56,8 +56,20 @@ auto test_exception = []<class... Args>(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 ", expected, "\nActual output   ", out, '\n'));
+};
+
 int main(int, char**) {
   print_tests(test_file, test_exception);
+  test_println_blank_line();
 
   return 0;
 }

>From 853bae680a36ee572eded08db6699d981d84ec62 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 5 Apr 2024 21:15:08 +0300
Subject: [PATCH 7/9] Addressed review comments

---
 .../ostream.formatted.print/locale-specific_form.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

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 b6c755a7a5776a..debe80f1a1920c 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
@@ -68,7 +68,7 @@ test(std::stringstream& stream, std::string expected, test_format_string<char, A
   // *** vprint_unicode ***
   {
     stream.str("");
-    ;
+
     std::vprint_unicode(stream, fmt.get(), std::make_format_args(args...));
     std::string out = stream.str();
     TEST_REQUIRE(out == expected,
@@ -78,7 +78,7 @@ test(std::stringstream& stream, std::string expected, test_format_string<char, A
   // *** vprint_nonunicode ***
   {
     stream.str("");
-    ;
+
     std::vprint_nonunicode(stream, fmt.get(), std::make_format_args(args...));
     std::string out = stream.str();
     TEST_REQUIRE(out == expected,
@@ -89,7 +89,7 @@ test(std::stringstream& stream, std::string expected, test_format_string<char, A
   {
     expected += '\n'; // Tested last since it changes the expected value.
     stream.str("");
-    ;
+
     std::println(stream, fmt, std::forward<Args>(args)...);
     std::string out = stream.str();
     TEST_REQUIRE(out == expected,
@@ -100,7 +100,7 @@ test(std::stringstream& stream, std::string expected, test_format_string<char, A
   {
     expected = "\n";
     stream.str("");
-    ;
+
     std::println(stream);
     std::string out = stream.str();
     TEST_REQUIRE(out == expected,

>From 32cfe093dda21eaa09618aac4a37b3c178ebedf4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 5 Apr 2024 21:43:48 +0300
Subject: [PATCH 8/9] Addressed review comments

---
 .../locale-specific_form.pass.cpp             |  2 +-
 .../ostream.formatted.print/println.pass.cpp  |  2 +-
 .../print.fun/println.blank_line.sh.cpp       | 48 +++++++++++++++++++
 3 files changed, 50 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/input.output/iostream.format/print.fun/println.blank_line.sh.cpp

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 debe80f1a1920c..89a5cbb3edf823 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
@@ -104,7 +104,7 @@ test(std::stringstream& stream, std::string expected, test_format_string<char, A
     std::println(stream);
     std::string out = stream.str();
     TEST_REQUIRE(out == expected,
-                 TEST_WRITE_CONCATENATED("\nExpected output ", expected, "\nActual output   ", out, '\n'));
+                 TEST_WRITE_CONCATENATED("\nExpected output (blank line) ", expected, "\nActual output   ", out, '\n'));
   }
 }
 
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 17aedc076cf2d0..19a02638a9da18 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
@@ -64,7 +64,7 @@ void test_println_blank_line() {
 
   std::string out = sstr.str();
   TEST_REQUIRE(out == expected,
-               TEST_WRITE_CONCATENATED("\nExpected output ", expected, "\nActual output   ", out, '\n'));
+               TEST_WRITE_CONCATENATED("\nExpected output (blank line) ", expected, "\nActual output   ", out, '\n'));
 };
 
 int main(int, char**) {
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 00000000000000..93c3563d501b20
--- /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
+
+// <print>
+
+//   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<class... Args>
+//   void println(FILE* stream, format_string<Args...> 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 <print>
+
+int main(int, char**) {
+  std::println();
+
+  return 0;
+}

>From 689a46e69b8aefef3ed6be115dbe79bd37314bd1 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 5 Apr 2024 22:00:07 +0300
Subject: [PATCH 9/9] Addressed review comments

---
 .../locale-specific_form.pass.cpp             | 49 +++++++++++++++----
 1 file changed, 39 insertions(+), 10 deletions(-)

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 89a5cbb3edf823..336be7455cf80d 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
@@ -96,16 +96,6 @@ test(std::stringstream& stream, std::string expected, test_format_string<char, A
                  TEST_WRITE_CONCATENATED(
                      "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
   }
-  // *** println blank line***
-  {
-    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'));
-  }
 }
 
 template <class... Args>
@@ -122,6 +112,7 @@ static void test(std::string expected, std::locale loc, test_format_string<char,
 }
 
 #ifndef TEST_HAS_NO_UNICODE
+
 struct numpunct_unicode : std::numpunct<char> {
   string_type do_truename() const override { return "gültig"; }
   string_type do_falsename() const override { return "ungültig"; }
@@ -2199,12 +2190,50 @@ static void test_floating_point() {
   test_floating_point_default_precision<F>();
 }
 
+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 loc = std::locale(std::locale(), new numpunct<char>());
+
+  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::global(loc);
+
+  test_println_blank_line(std::locale(LOCALE_en_US_UTF_8));
+  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<float>();
   test_floating_point<double>();
   test_floating_point<long double>();
+  test_println_blank_line();
 
   return 0;
 }



More information about the libcxx-commits mailing list