Skip to content
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;
}