Skip to content

[libc++][format] P3142R0: Printing Blank Lines with println #87277

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

Merged
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/19.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``

Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cPapers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -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","|Complete|","19.0",""
"`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|"
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/ostream
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1163,6 +1164,9 @@ _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

_LIBCPP_END_NAMESPACE_STD
Expand Down
8 changes: 8 additions & 0 deletions libcxx/include/print
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -356,6 +358,12 @@ _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>
_LIBCPP_HIDE_FROM_ABI void println(format_string<_Args...> __fmt, _Args&&... __args) {
std::println(stdout, __fmt, std::forward<_Args>(__args)...);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -67,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,
Expand All @@ -77,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,
Expand All @@ -88,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,
Expand All @@ -111,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"; }
Expand Down Expand Up @@ -2188,12 +2190,47 @@ 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::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<char>());
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<float>();
test_floating_point<double>();
test_floating_point<long double>();
test_println_blank_line();

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -55,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 (blank line) ", expected, "\nActual output ", out, '\n'));
};

int main(int, char**) {
print_tests(test_file, test_exception);
test_println_blank_line();

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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<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) == "\n");
}

static void test_vprint_unicode() {
std::array<char, 100> buffer{0};

Expand Down Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -137,6 +160,7 @@ int main(int, char**) {
#endif
test_read_only();
test_new_line();
test_println_blank_line();

return 0;
}