1
/**************************************************************************
3
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
4
License: See LICENSE_zlib.txt file
5
***************************************************************************/
7
#ifndef LIBDEBUG_DSTREAM_H
8
#define LIBDEBUG_DSTREAM_H
10
#include <ostream> // std::ostream definition
11
#include <streambuf> // std::streambuf definition
16
#include "hz/intrusive_ptr.h"
24
namespace debug_internal {
27
std::streambuf& get_null_streambuf();
29
std::ostream& get_null_stream();
32
// state.h includes us, so we need these forward declarations
34
// DebugState& get_debug_state();
41
class DebugStreamBuf : public std::streambuf {
44
DebugStreamBuf(DebugOutStream* dos) : dos_(dos)
46
// in case of overflow for output, overflow() will be called to _output_ the data.
48
// no buffers here - we process it char-by-char. Apart from practical reasons,
49
// this is also needed to ensure that the state of the object (buffer) doesn't
50
// change during calls to methods - to ensure read-only thread-safety.
53
char* buf = new char[buf_size]; // further accessible through pbase().
54
setp(buf, buf + buf_size); // Set output sequence pointers, aka output buffer
59
setg(NULL, NULL, NULL); // Set input sequence pointers; not relevant in this class.
63
virtual ~DebugStreamBuf()
66
delete[] pbase(); // delete the buffer
70
// Force output of the stringstream's contents to the channels.
71
// This is function thread-safe as long as state is not modified.
80
// overflow happens when a new character is to be written at the put
81
// pointer pptr position, but this has reached the end pointer epptr.
82
virtual int overflow(int c) // overridden from parent
84
sync(); // write the buffer contents if available
85
if (c != traits_type::eof()) {
86
if (pbase() == epptr()) { // no buffer, write it char-by-char (epptr() - buffer end pointer)
91
} else { // we have a buffer
92
// put c into buffer (the overflowed char); the rest is written in sync() earlier.
93
sputc(static_cast<char>(c));
100
// sort-of flush the buffer. only makes sense if there is a buffer.
101
virtual int sync() // overridden from parent
103
if (pbase() != pptr()) { // pptr() - current position; condition is true only if there is something in the buffer.
104
// write_out(std::string(pbase(), pptr() - pbase()));
105
for (char* pos = pbase(); pos != pptr(); ++pos)
107
setp(pbase(), epptr()); // reset the buffer's current pointer (?)
113
// custom function. write contents if necessary.
114
// void write_out(const std::string& str) // str may be one char, or entire buffer, or in between.
119
// custom function. write contents if necessary.
120
void write_char(char c)
123
oss_.reset(new std::ostringstream());
126
if (c == '\n') // send to channels on newline
131
// This is function thread-safe as long as state is not modified.
132
void flush_to_channel();
136
DebugOutStream* dos_;
138
// It's thread-local because it is not shared between different flows.
139
// we can't provide any manual cleanup, because the only one we can do it
140
// is in main thread, and it's already being done with the destructor.
141
hz::thread_local_ptr<std::ostringstream> oss_;
143
DebugStreamBuf(const DebugStreamBuf& from);
149
typedef std::vector<debug_channel_base_ptr> channel_list_t;
153
// This is returned by debug_out()
154
class DebugOutStream : public std::ostream, public hz::intrusive_ptr_referenced {
157
friend class DebugStreamBuf;
159
DebugOutStream(debug_level::flag level, const std::string& domain, const debug_format::type& format_flags)
160
: std::ostream(NULL), level_(level), domain_(domain), format_(format_flags), buf_(this)
162
set_enabled(true); // sets ostream's rdbuf
165
// DebugOutStream() : std::ostream(NULL), buf_(this)
167
// set_enabled(false);
170
DebugOutStream(const DebugOutStream& other, const std::string& domain)
171
: std::ostream(NULL), level_(other.level_), domain_(domain), format_(other.format_), buf_(this)
173
set_enabled(other.get_enabled()); // sets ostream's rdbuf
174
for (channel_list_t::const_iterator iter = other.channels_.begin(); iter != other.channels_.end(); ++iter) {
175
// we let the object dictate the copy rules because really copying it
176
// may harm the underlying locking mechanism
177
channels_.push_back((*iter)->clone_ptr());
182
void set_level(debug_level::flag level)
187
void set_domain(const std::string& domain)
193
void set_format(const debug_format::type& format_flags)
195
format_ = format_flags;
198
debug_format::type get_format() const
204
void set_enabled(bool enabled)
209
rdbuf(&get_null_streambuf());
212
bool get_enabled() const
214
return (rdbuf() == &buf_);
218
void set_channels(const channel_list_t& channels)
220
channels_ = channels;
223
channel_list_t& get_channels()
228
// this will claim the ownership of the passed parameter
229
void add_channel(debug_channel_base_ptr channel)
231
channels_.push_back(channel);
235
bool get_is_first_line()
237
if (!is_first_line_.get())
238
is_first_line_.reset(new bool(true));
239
return *is_first_line_;
242
void set_is_first_line(bool b)
244
if (!is_first_line_.get()) {
245
is_first_line_.reset(new bool(b));
252
// Force output of buf_'s contents to the channels.
253
// This also outputs a prefix if needed.
254
// This is function thread-safe in read-only context.
255
std::ostream& force_output()
263
debug_level::flag level_;
265
debug_format::type format_;
267
// It's thread-local because it is not shared between different flows.
268
// we can't provide any manual cleanup, because the only one we can do
269
// is in main thread, and it's already being done with the destructor.
270
hz::thread_local_ptr<bool> is_first_line_;
272
channel_list_t channels_;
274
DebugStreamBuf buf_; // not thread-local, but its buffer is.
281
} // ns debug_internal