Skip to content

Commit

Permalink
Enhance buffering and target stream handling of iostreams
Browse files Browse the repository at this point in the history
- buffering may be independent of tied streams, so make it an extra param
- the target output stream might be out, error or log

The latter is important as cerr and clog share the underlying buffer
which avoids mangled output.
This is consistent with the stdlib implementation.
  • Loading branch information
Flamefire committed Oct 18, 2024
1 parent 4f4b7ab commit a769dd0
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 17 deletions.
8 changes: 7 additions & 1 deletion include/boost/nowide/iostream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ namespace nowide {
class BOOST_NOWIDE_DECL winconsole_ostream : public std::ostream
{
public:
winconsole_ostream(bool isBuffered, winconsole_ostream* tieStream);
enum class target_stream
{
output,
error,
log,
};
winconsole_ostream(target_stream target, bool isBuffered, winconsole_ostream* tieStream);
~winconsole_ostream();

private:
Expand Down
43 changes: 27 additions & 16 deletions src/iostream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,27 +82,37 @@ namespace nowide {
}
};

winconsole_ostream::winconsole_ostream(const bool isBuffered, winconsole_ostream* tieStream) : std::ostream(0)
winconsole_ostream::winconsole_ostream(const target_stream target,
const bool isBuffered,
winconsole_ostream* tieStream) :
std::ostream(nullptr)
{
HANDLE h;
if(isBuffered)
h = GetStdHandle(STD_OUTPUT_HANDLE);
else
h = GetStdHandle(STD_ERROR_HANDLE);
const HANDLE h = GetStdHandle(target == target_stream::output ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);

if(is_atty_handle(h))
{
d.reset(new console_output_buffer(h));
std::ostream::rdbuf(d.get());
if(target == target_stream::log)
rdbuf(boost::nowide::cerr.rdbuf()); // clog and cerr use the same buffer
else
{
d.reset(new console_output_buffer(h));
rdbuf(d.get());
}
} else
{
std::ostream::rdbuf(isBuffered ? std::cout.rdbuf() : std::cerr.rdbuf());
switch(target)
{
case target_stream::error: rdbuf(std::cerr.rdbuf()); break;
case target_stream::log: rdbuf(std::clog.rdbuf()); break;
case target_stream::output: rdbuf(std::cout.rdbuf()); break;
}
assert(rdbuf());
}

if(tieStream)
{
tie(tieStream);
setf(ios_base::unitbuf); // If tieStream is set, this is cerr -> set unbuffered
}
if(!isBuffered)
setf(ios_base::unitbuf);
}
winconsole_ostream::~winconsole_ostream()
{
Expand Down Expand Up @@ -134,7 +144,7 @@ namespace nowide {

} // namespace detail

// Make sure those are initialized as early as possible
// Make sure those are initialized as early as possible
#ifdef BOOST_MSVC
#pragma warning(disable : 4073)
#pragma init_seg(lib)
Expand All @@ -144,10 +154,11 @@ namespace nowide {
#else
#define BOOST_NOWIDE_INIT_PRIORITY
#endif
detail::winconsole_ostream cout BOOST_NOWIDE_INIT_PRIORITY(true, nullptr);
using target_stream = detail::winconsole_ostream::target_stream;
detail::winconsole_ostream cout BOOST_NOWIDE_INIT_PRIORITY(target_stream::output, true, nullptr);
detail::winconsole_istream cin BOOST_NOWIDE_INIT_PRIORITY(&cout);
detail::winconsole_ostream cerr BOOST_NOWIDE_INIT_PRIORITY(false, &cout);
detail::winconsole_ostream clog BOOST_NOWIDE_INIT_PRIORITY(false, nullptr);
detail::winconsole_ostream cerr BOOST_NOWIDE_INIT_PRIORITY(target_stream::error, false, &cout);
detail::winconsole_ostream clog BOOST_NOWIDE_INIT_PRIORITY(target_stream::log, true, nullptr);
} // namespace nowide
} // namespace boost

Expand Down

0 comments on commit a769dd0

Please sign in to comment.