Skip to content

Commit e7de8c3

Browse files
committed
Fix flushing C++ iostreams before calling write_console()
This change correctly implements https://wg21.link/P2539/ for both C streams and C++ iostreams. Fixes fmtlib#3688.
1 parent 3b7f58a commit e7de8c3

File tree

3 files changed

+31
-14
lines changed

3 files changed

+31
-14
lines changed

include/fmt/format-inl.h

+10-6
Original file line numberDiff line numberDiff line change
@@ -1425,16 +1425,13 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {
14251425

14261426
namespace detail {
14271427
#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR)
1428-
FMT_FUNC bool write_console(std::FILE*, string_view) { return false; }
1428+
FMT_FUNC bool write_console(int, string_view) { return false; }
14291429
#else
14301430
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
14311431
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
14321432
void*, const void*, dword, dword*, void*);
14331433

1434-
FMT_FUNC bool write_console(std::FILE* f, string_view text) {
1435-
int fd = _fileno(f);
1436-
if (!_isatty(fd)) return false;
1437-
std::fflush(f);
1434+
FMT_FUNC bool write_console(int fd, string_view text) {
14381435
auto u16 = utf8_to_utf16(text);
14391436
return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
14401437
static_cast<dword>(u16.size()), nullptr, nullptr) != 0;
@@ -1451,7 +1448,14 @@ FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
14511448
#endif
14521449

14531450
FMT_FUNC void print(std::FILE* f, string_view text) {
1454-
if (!write_console(f, text)) fwrite_fully(text.data(), text.size(), f);
1451+
#ifdef _WIN32
1452+
int fd = _fileno(f);
1453+
if (_isatty(fd)) {
1454+
std::fflush(f);
1455+
if (write_console(fd, text)) return;
1456+
}
1457+
#endif
1458+
fwrite_fully(text.data(), text.size(), f);
14551459
}
14561460
} // namespace detail
14571461

include/fmt/format.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@ struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
10381038

10391039
FMT_END_EXPORT
10401040
namespace detail {
1041-
FMT_API bool write_console(std::FILE* f, string_view text);
1041+
FMT_API bool write_console(int fd, string_view text);
10421042
FMT_API void print(std::FILE*, string_view);
10431043
} // namespace detail
10441044

include/fmt/ostream.h

+20-7
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010

1111
#include <fstream> // std::filebuf
1212

13-
#if defined(_WIN32) && defined(__GLIBCXX__)
13+
#ifdef _WIN32
14+
#ifdef __GLIBCXX__
1415
# include <ext/stdio_filebuf.h>
1516
# include <ext/stdio_sync_filebuf.h>
1617
#endif
18+
#include <io.h>
19+
#endif
1720

1821
#include "format.h"
1922

@@ -38,21 +41,31 @@ auto get_file(std::filebuf&) -> FILE*;
3841
#endif
3942

4043
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
44+
FILE* f = nullptr;
4145
#if FMT_MSC_VERSION
4246
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
43-
if (FILE* f = get_file(*buf)) return write_console(f, data);
47+
f = get_file(*buf);
48+
else
49+
return false;
4450
#elif defined(_WIN32) && defined(__GLIBCXX__)
4551
auto* rdbuf = os.rdbuf();
46-
FILE* c_file;
4752
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
48-
c_file = sfbuf->file();
53+
f = sfbuf->file();
4954
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
50-
c_file = fbuf->file();
55+
f = fbuf->file();
5156
else
5257
return false;
53-
if (c_file) return write_console(c_file, data);
5458
#else
55-
ignore_unused(os, data);
59+
ignore_unused(os, data, f);
60+
#endif
61+
#ifdef _WIN32
62+
if (f) {
63+
int fd = _fileno(f);
64+
if (_isatty(fd)) {
65+
os.flush();
66+
return write_console(fd, data);
67+
}
68+
}
5669
#endif
5770
return false;
5871
}

0 commit comments

Comments
 (0)