Skip to content

Commit 7f9f82e

Browse files
authored
[libc++][format] P3142R0: Printing Blank Lines with println (llvm#87277)
Implements https://wg21.link/P3142R0 Applied retroactively as DR, same as stdlibc++ and MS STL: https://github.com/orgs/microsoft/projects/1143?pane=issue&itemId=57457187
1 parent b88a1dd commit 7f9f82e

File tree

9 files changed

+156
-4
lines changed

9 files changed

+156
-4
lines changed

libcxx/docs/ReleaseNotes/19.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Implemented Papers
4444
- P2495R3 - Interfacing ``stringstream``\s with ``string_view``
4545
- P2867R2 - Remove Deprecated ``strstream``\s From C++26
4646
- P2872R3 - Remove ``wstring_convert`` From C++26
47+
- P3142R0 - Printing Blank Lines with ``println`` (as DR against C++23)
4748
- P2302R4 - ``std::ranges::contains``
4849
- P1659R3 - ``std::ranges::starts_with`` and ``std::ranges::ends_with``
4950

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"`P2869R4 <https://wg21.link/P2869R4>`__","LWG","Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26","Tokyo March 2024","","",""
5252
"`P2872R3 <https://wg21.link/P2872R3>`__","LWG","Remove ``wstring_convert`` From C++26","Tokyo March 2024","|Complete|","19.0",""
5353
"`P3107R5 <https://wg21.link/P3107R5>`__","LWG","Permit an efficient implementation of ``std::print``","Tokyo March 2024","","","|format| |DR|"
54-
"`P3142R0 <https://wg21.link/P3142R0>`__","LWG","Printing Blank Lines with ``println``","Tokyo March 2024","","","|format|"
54+
"`P3142R0 <https://wg21.link/P3142R0>`__","LWG","Printing Blank Lines with ``println``","Tokyo March 2024","|Complete|","19.0","|format|"
5555
"`P2845R8 <https://wg21.link/P2845R8>`__","LWG","Formatting of ``std::filesystem::path``","Tokyo March 2024","","","|format|"
5656
"`P0493R5 <https://wg21.link/P0493R5>`__","LWG","Atomic minimum/maximum","Tokyo March 2024","","",""
5757
"`P2542R8 <https://wg21.link/P2542R8>`__","LWG","``views::concat``","Tokyo March 2024","","","|ranges|"

libcxx/include/ostream

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ template<class... Args>
164164
void print(ostream& os, format_string<Args...> fmt, Args&&... args);
165165
template<class... Args> // since C++23
166166
void println(ostream& os, format_string<Args...> fmt, Args&&... args);
167+
void println(ostream& os); // since C++26
167168
168169
void vprint_unicode(ostream& os, string_view fmt, format_args args); // since C++23
169170
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,
11631164
# endif // _LIBCPP_HAS_NO_UNICODE
11641165
}
11651166

1167+
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
1168+
_LIBCPP_HIDE_FROM_ABI inline void println(ostream& __os) { std::print(__os, "\n"); }
1169+
11661170
#endif // _LIBCPP_STD_VER >= 23
11671171

11681172
_LIBCPP_END_NAMESPACE_STD

libcxx/include/print

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ namespace std {
1515
// [print.fun], print functions
1616
template<class... Args>
1717
void print(format_string<Args...> fmt, Args&&... args);
18+
void println(); // Since C++26
1819
template<class... Args>
1920
void print(FILE* stream, format_string<Args...> fmt, Args&&... args);
21+
void println(FILE* stream); // Since C++26
2022
2123
template<class... Args>
2224
void println(format_string<Args...> fmt, Args&&... args);
@@ -356,6 +358,12 @@ _LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt
356358
# endif // _LIBCPP_HAS_NO_UNICODE
357359
}
358360

361+
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
362+
_LIBCPP_HIDE_FROM_ABI inline void println(FILE* __stream) { std::print(__stream, "\n"); }
363+
364+
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
365+
_LIBCPP_HIDE_FROM_ABI inline void println() { println(stdout); }
366+
359367
template <class... _Args>
360368
_LIBCPP_HIDE_FROM_ABI void println(format_string<_Args...> __fmt, _Args&&... __args) {
361369
std::println(stdout, __fmt, std::forward<_Args>(__args)...);

libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
// void print(ostream& os, format_string<Args...> fmt, Args&&... args);
2727
// template<class... Args>
2828
// void println(ostream& os, format_string<Args...> fmt, Args&&... args);
29+
// void println(ostream& os); // since C++26
2930
//
3031
// void vprint_unicode(ostream& os, string_view fmt, format_args args);
3132
// 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<char, A
6768
// *** vprint_unicode ***
6869
{
6970
stream.str("");
70-
;
71+
7172
std::vprint_unicode(stream, fmt.get(), std::make_format_args(args...));
7273
std::string out = stream.str();
7374
TEST_REQUIRE(out == expected,
@@ -77,7 +78,7 @@ test(std::stringstream& stream, std::string expected, test_format_string<char, A
7778
// *** vprint_nonunicode ***
7879
{
7980
stream.str("");
80-
;
81+
8182
std::vprint_nonunicode(stream, fmt.get(), std::make_format_args(args...));
8283
std::string out = stream.str();
8384
TEST_REQUIRE(out == expected,
@@ -88,7 +89,7 @@ test(std::stringstream& stream, std::string expected, test_format_string<char, A
8889
{
8990
expected += '\n'; // Tested last since it changes the expected value.
9091
stream.str("");
91-
;
92+
9293
std::println(stream, fmt, std::forward<Args>(args)...);
9394
std::string out = stream.str();
9495
TEST_REQUIRE(out == expected,
@@ -111,6 +112,7 @@ static void test(std::string expected, std::locale loc, test_format_string<char,
111112
}
112113

113114
#ifndef TEST_HAS_NO_UNICODE
115+
114116
struct numpunct_unicode : std::numpunct<char> {
115117
string_type do_truename() const override { return "gültig"; }
116118
string_type do_falsename() const override { return "ungültig"; }
@@ -2188,12 +2190,47 @@ static void test_floating_point() {
21882190
test_floating_point_default_precision<F>();
21892191
}
21902192

2193+
static void test_println_blank_line(std::stringstream& stream) {
2194+
std::string expected{'\n'};
2195+
stream.str("");
2196+
2197+
std::println(stream);
2198+
std::string out = stream.str();
2199+
TEST_REQUIRE(out == expected,
2200+
TEST_WRITE_CONCATENATED("\nExpected output (blank line) ", expected, "\nActual output ", out, '\n'));
2201+
}
2202+
2203+
static void test_println_blank_line(std::locale loc) {
2204+
std::stringstream stream;
2205+
stream.imbue(loc);
2206+
test_println_blank_line(stream);
2207+
}
2208+
2209+
static void test_println_blank_line() {
2210+
std::locale::global(std::locale(LOCALE_en_US_UTF_8));
2211+
assert(std::locale().name() == LOCALE_en_US_UTF_8);
2212+
std::stringstream stream;
2213+
test_println_blank_line(stream);
2214+
2215+
std::locale loc = std::locale(std::locale(), new numpunct<char>());
2216+
std::locale::global(loc);
2217+
test_println_blank_line(std::locale(LOCALE_en_US_UTF_8));
2218+
2219+
#ifndef TEST_HAS_NO_UNICODE
2220+
2221+
std::locale loc_unicode = std::locale(std::locale(), new numpunct_unicode());
2222+
test_println_blank_line(loc_unicode);
2223+
2224+
#endif // TEST_HAS_NO_UNICODE
2225+
}
2226+
21912227
int main(int, char**) {
21922228
test_bool();
21932229
test_integer();
21942230
test_floating_point<float>();
21952231
test_floating_point<double>();
21962232
test_floating_point<long double>();
2233+
test_println_blank_line();
21972234

21982235
return 0;
21992236
}

libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/println.pass.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
// template<class... Args>
1919
// void println(ostream& os, format_string<Args...> fmt, Args&&... args);
20+
// void println(ostream& os); // since C++26
2021

2122
// [ostream.formatted.print]/3
2223
// If the function is vprint_unicode and os is a stream that refers to
@@ -55,8 +56,20 @@ auto test_exception = []<class... Args>(std::string_view, std::string_view, Args
5556
// The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
5657
};
5758

59+
void test_println_blank_line() {
60+
std::string expected{'\n'};
61+
62+
std::stringstream sstr;
63+
std::println(sstr);
64+
65+
std::string out = sstr.str();
66+
TEST_REQUIRE(out == expected,
67+
TEST_WRITE_CONCATENATED("\nExpected output (blank line) ", expected, "\nActual output ", out, '\n'));
68+
};
69+
5870
int main(int, char**) {
5971
print_tests(test_file, test_exception);
72+
test_println_blank_line();
6073

6174
return 0;
6275
}

libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525

2626
// template<class... Args>
2727
// void print(FILE* stream, format_string<Args...> fmt, Args&&... args);
28+
// void println(); // Since C++26
2829
// template<class... Args>
2930
// void println(FILE* stream, format_string<Args...> fmt, Args&&... args);
31+
// void println(FILE* stream); // Since C++26
3032
// void vprint_unicode(FILE* stream, string_view fmt, format_args args);
3133
// void vprint_nonunicode(FILE* stream, string_view fmt, format_args args);
3234

@@ -63,6 +65,20 @@ static void test_println() {
6365
assert(std::string_view(buffer.data(), pos) == "hello world!\n");
6466
}
6567

68+
static void test_println_blank_line() {
69+
std::array<char, 100> buffer{0};
70+
71+
FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
72+
assert(file);
73+
74+
std::println(file);
75+
long pos = std::ftell(file);
76+
std::fclose(file);
77+
78+
assert(pos > 0);
79+
assert(std::string_view(buffer.data(), pos) == "\n");
80+
}
81+
6682
static void test_vprint_unicode() {
6783
std::array<char, 100> buffer{0};
6884

@@ -96,6 +112,7 @@ static void test_vprint_nonunicode() {
96112
int main(int, char**) {
97113
test_print();
98114
test_println();
115+
test_println_blank_line();
99116
test_vprint_unicode();
100117
test_vprint_nonunicode();
101118

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===----------------------------------------------------------------------===//
2+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3+
// See https://llvm.org/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//
6+
//===----------------------------------------------------------------------===//
7+
8+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
9+
// UNSUPPORTED: no-filesystem
10+
// UNSUPPORTED: executor-has-no-bash
11+
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
12+
13+
// FIXME PRINT How to test println on Windows?
14+
// XFAIL: msvc, target={{.+}}-windows-gnu
15+
16+
// XFAIL: availability-fp_to_chars-missing
17+
18+
// <print>
19+
20+
// void println();
21+
22+
// Testing this properly is quite hard; the function unconditionally
23+
// writes to stdout. When stdout is redirected to a file it is no longer
24+
// considered a terminal. The function is a small wrapper around
25+
//
26+
// template<class... Args>
27+
// void println(FILE* stream, format_string<Args...> fmt, Args&&... args);
28+
//
29+
// So do minimal tests for this function and rely on the FILE* overload
30+
// to do more testing.
31+
//
32+
// The testing is based on the testing for std::cout.
33+
34+
// TODO PRINT Use lit builtin echo
35+
36+
// FILE_DEPENDENCIES: echo.sh
37+
// RUN: %{build}
38+
// RUN: %{exec} bash echo.sh -ne "\n" > %t.expected
39+
// RUN: %{exec} "%t.exe" > %t.actual
40+
// RUN: diff -u %t.actual %t.expected
41+
42+
#include <print>
43+
44+
int main(int, char**) {
45+
std::println();
46+
47+
return 0;
48+
}

libcxx/test/std/input.output/iostream.format/print.fun/println.file.pass.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,29 @@ static void test_new_line() {
129129
}
130130
}
131131

132+
static void test_println_blank_line() {
133+
// Text does newline translation.
134+
{
135+
FILE* file = fopen(filename.c_str(), "w");
136+
assert(file);
137+
138+
std::println(file);
139+
#ifndef _WIN32
140+
assert(std::ftell(file) == 1);
141+
#else
142+
assert(std::ftell(file) == 2);
143+
#endif
144+
}
145+
// Binary no newline translation.
146+
{
147+
FILE* file = fopen(filename.c_str(), "wb");
148+
assert(file);
149+
150+
std::println(file);
151+
assert(std::ftell(file) == 1);
152+
}
153+
}
154+
132155
int main(int, char**) {
133156
print_tests(test_file, test_exception);
134157

@@ -137,6 +160,7 @@ int main(int, char**) {
137160
#endif
138161
test_read_only();
139162
test_new_line();
163+
test_println_blank_line();
140164

141165
return 0;
142166
}

0 commit comments

Comments
 (0)