diff --git a/stl/inc/ostream b/stl/inc/ostream index 3ce22b869b..b682a8b68b 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -1272,6 +1272,11 @@ void println(ostream& _Ostr, const format_string<_Types...> _Fmt, _Types&&... _A _STD _Print_impl(_Add_newline::_Yes, _Ostr, _Fmt, _STD forward<_Types>(_Args)...); } +_EXPORT_STD template // improves throughput, see GH-2329 +void println(ostream& _Ostr) { + _STD print(_Ostr, "\n"); +} + _EXPORT_STD template // improves throughput, see GH-2329 void vprint_unicode(ostream& _Ostr, const string_view _Fmt_str, const format_args _Fmt_args) { _STD _Vprint_unicode_impl(_Add_newline::_Nope, _Ostr, _Fmt_str, _Fmt_args); diff --git a/stl/inc/print b/stl/inc/print index 3d19c7aa4f..13f12ea046 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -131,6 +131,16 @@ void println(FILE* const _Stream, const format_string<_Types...> _Fmt, _Types&&. _STD _Print_impl(_Add_newline::_Yes, _Stream, _Fmt, _STD forward<_Types>(_Args)...); } +_EXPORT_STD template // improves throughput, see GH-2329 +void println(FILE* _Stream) { + _STD print(_Stream, "\n"); +} + +_EXPORT_STD template // improves throughput, see GH-2329 +void println() { + _STD println(stdout); +} + _EXPORT_STD template void println(const format_string<_Types...> _Fmt, _Types&&... _Args) { _STD println(stdout, _Fmt, _STD forward<_Types>(_Args)...); diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index f182ba62c5..9d4c21a536 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -384,6 +384,7 @@ // P2713R1 Escaping Improvements In std::format // P2763R1 Fixing layout_stride's Default Constructor For Fully Static Extents // P2836R1 basic_const_iterator Should Follow Its Underlying Type's Convertibility +// P3142R0 Printing Blank Lines With println() // _HAS_CXX23 and _SILENCE_ALL_CXX23_DEPRECATION_WARNINGS control: // P1413R3 Deprecate aligned_storage And aligned_union diff --git a/tests/std/include/test_header_units_and_modules.hpp b/tests/std/include/test_header_units_and_modules.hpp index 25576a13c3..ceab5eb2d5 100644 --- a/tests/std/include/test_header_units_and_modules.hpp +++ b/tests/std/include/test_header_units_and_modules.hpp @@ -545,9 +545,11 @@ void test_print() { using namespace std; puts("Testing ."); println("Hello, world!"); + println(); #ifdef _CPPRTTI println(cout, "The answer to life, the universe, and everything: {}", 42); + println(cout); #endif // _CPPRTTI } #endif // TEST_STANDARD >= 23 diff --git a/tests/std/tests/P2093R14_formatted_output/test.cpp b/tests/std/tests/P2093R14_formatted_output/test.cpp index 68b9ff5d28..0cf9224309 100644 --- a/tests/std/tests/P2093R14_formatted_output/test.cpp +++ b/tests/std/tests/P2093R14_formatted_output/test.cpp @@ -403,17 +403,19 @@ void test_invalid_code_points_console() { test_invalid_sequence_closure("\xF0\x28\x8C\x25"); } +FILE* checked_fopen_s(const string& filename, const char* const mode) { + FILE* ret; + const errno_t fopen_result = fopen_s(&ret, filename.c_str(), mode); + assert(fopen_result == 0); + return ret; +} + void test_invalid_code_points_file() { // Unlike for the console API when the ordinary literal encoding is UTF-8, invalid code points shouldn't // be replaced when writing to a file. const string temp_file_name_str = temp_file_name(); - FILE* temp_file_stream; - - { - const errno_t fopen_result = fopen_s(&temp_file_stream, temp_file_name_str.c_str(), "w+b"); - assert(fopen_result == 0); - } + FILE* temp_file_stream = checked_fopen_s(temp_file_name_str, "w+b"); using printed_string_type = format_string<>; @@ -509,6 +511,38 @@ void test_stream_flush_console() { const wstring extractedStr{temp_console.get_console_line(0)}; assert(extractedStr == L"Hello, world!"); } + + print(console_file_stream, "kitty"); + println(console_file_stream); + println(console_file_stream, "cat"); + + { + const wstring line0{temp_console.get_console_line(0)}; + const wstring line1{temp_console.get_console_line(1)}; + const wstring line2{temp_console.get_console_line(2)}; + + assert(line0 == L"Hello, world!"); + + if constexpr (_Is_ordinary_literal_encoding_utf8()) { + assert(line1 == L"kitty"); + assert(line2 == L"cat"); + } else { + assert(line1.empty()); + assert(line2.empty()); + } + } + + maybe_flush_console_file_stream(temp_console); + + { + const wstring line0{temp_console.get_console_line(0)}; + const wstring line1{temp_console.get_console_line(1)}; + const wstring line2{temp_console.get_console_line(2)}; + + assert(line0 == L"Hello, world!"); + assert(line1 == L"kitty"); + assert(line2 == L"cat"); + } } void test_stream_flush_file() { @@ -567,6 +601,7 @@ void test_empty_strings_and_newlines() { print(output_file_stream, "-D\n"); print(output_file_stream, "{{}} for {}!\n", "impact"); + println(output_file_stream); println(output_file_stream, "I have {} cute {} kittens.", 1729, "fluffy"); println(output_file_stream, ""); println(output_file_stream, "What are an orthodontist's favorite characters? '{{' and '}}', of course!"); @@ -574,6 +609,20 @@ void test_empty_strings_and_newlines() { println(output_file_stream, "THREE"); } + { + FILE* temp_file_stream = checked_fopen_s(temp_file_name_str, "a"); + + print(temp_file_stream, "space"); + print(temp_file_stream, ""); + print(temp_file_stream, "time\n"); + + println(temp_file_stream, "general"); + println(temp_file_stream); + println(temp_file_stream, "relativity"); + + fclose(temp_file_stream); + } + { ifstream input_file_stream{temp_file_name_str}; @@ -585,6 +634,7 @@ void test_empty_strings_and_newlines() { const vector expected_lines{ "NCC-1701-D", "{} for impact!", + "", "I have 1729 cute fluffy kittens.", "", "What are an orthodontist's favorite characters? '{' and '}', of course!", @@ -592,6 +642,10 @@ void test_empty_strings_and_newlines() { "TWO", "", "THREE", + "spacetime", + "general", + "", + "relativity", }; assert(lines == expected_lines);