mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-10-01 08:05:52 +00:00
292 lines
6.6 KiB
C++
292 lines
6.6 KiB
C++
/******************************************************************************
|
|
License: Zlib
|
|
Copyright:
|
|
(C) 2008 - 2021 Alexander Shaduri <ashaduri@gmail.com>
|
|
******************************************************************************/
|
|
/// \file
|
|
/// \author Alexander Shaduri
|
|
/// \ingroup libdebug
|
|
/// \weakgroup libdebug
|
|
/// @{
|
|
|
|
#ifndef LIBDEBUG_DSTREAM_H
|
|
#define LIBDEBUG_DSTREAM_H
|
|
|
|
#include <ostream> // std::ostream definition
|
|
#include <streambuf> // std::streambuf definition
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "dflags.h"
|
|
#include "dchannel.h"
|
|
|
|
|
|
|
|
namespace debug_internal {
|
|
|
|
|
|
/// Get null streambuf - a streambuf which does nothing.
|
|
std::streambuf& get_null_streambuf();
|
|
|
|
/// Get null ostream - an ostream which does nothing.
|
|
std::ostream& get_null_stream();
|
|
|
|
|
|
// state.h includes us, so we need these forward declarations
|
|
// class DebugState;
|
|
// DebugState& get_debug_state();
|
|
|
|
|
|
|
|
class DebugOutStream;
|
|
|
|
|
|
/// Streambuf for libdebug, used in DebugOutStream implementation.
|
|
class DebugStreamBuf : public std::streambuf {
|
|
public:
|
|
|
|
/// Constructor
|
|
explicit DebugStreamBuf(DebugOutStream* dos) : dos_(dos)
|
|
{
|
|
// in case of overflow for output, overflow() will be called to _output_ the data.
|
|
|
|
// no buffers here - we process it char-by-char.
|
|
// std::size_t buf_size = 0;
|
|
// if (buf_size) {
|
|
// char* buf = new char[buf_size]; // further accessible through pbase().
|
|
// setp(buf, buf + buf_size); // Set output sequence pointers, aka output buffer
|
|
// } else {
|
|
setp(nullptr, nullptr);
|
|
// }
|
|
|
|
setg(nullptr, nullptr, nullptr); // Set input sequence pointers; not relevant in this class.
|
|
}
|
|
|
|
|
|
/// Disallow copying
|
|
DebugStreamBuf(const DebugStreamBuf& from) = delete;
|
|
|
|
|
|
/// Virtual destructor
|
|
~DebugStreamBuf() override
|
|
{
|
|
sync();
|
|
delete[] pbase(); // delete the buffer
|
|
}
|
|
|
|
|
|
/// Force output of the stringstream's contents to the channels.
|
|
void force_output()
|
|
{
|
|
flush_to_channel();
|
|
}
|
|
|
|
|
|
protected:
|
|
|
|
/// Overflow happens when a new character is to be written at the put
|
|
/// pointer pptr position, but this has reached the end pointer epptr.
|
|
/// Reimplemented.
|
|
int overflow(int c) override
|
|
{
|
|
sync(); // write the buffer contents if available
|
|
if (c != traits_type::eof()) {
|
|
if (pbase() == epptr()) { // no buffer, write it char-by-char (epptr() - buffer end pointer)
|
|
// std::string tmp;
|
|
// tmp += char(c);
|
|
// write_out(tmp);
|
|
write_char(char(c));
|
|
} else { // we have a buffer
|
|
// put c into buffer (the overflowed char); the rest is written in sync() earlier.
|
|
sputc(static_cast<char>(c));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/// Sort-of flush the buffer. Only makes sense if there is a buffer.
|
|
/// Reimplemented.
|
|
int sync() final
|
|
{
|
|
if (pbase() != pptr()) { // pptr() - current position; condition is true only if there is something in the buffer.
|
|
// write_out(std::string(pbase(), pptr() - pbase()));
|
|
for (char* pos = pbase(); pos != pptr(); ++pos)
|
|
write_char(*pos);
|
|
setp(pbase(), epptr()); // reset the buffer's current pointer (?)
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/// Write contents if necessary.
|
|
void write_char(char c)
|
|
{
|
|
oss_ << c;
|
|
if (c == '\n') // send to channels on newline
|
|
flush_to_channel();
|
|
}
|
|
|
|
|
|
/// Flush contents to debug channel.
|
|
void flush_to_channel();
|
|
|
|
|
|
private:
|
|
|
|
DebugOutStream* dos_ = nullptr; ///< Debug output stream
|
|
|
|
std::ostringstream oss_; ///< A buffer for output storage.
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Debug output stream (inherits std::ostream).
|
|
/// This is returned by debug_out().
|
|
class DebugOutStream : public std::ostream {
|
|
public:
|
|
|
|
friend class DebugStreamBuf;
|
|
|
|
/// Constructor
|
|
DebugOutStream(debug_level::flag level, std::string domain, const debug_format::flags& format_flags)
|
|
: std::ostream(nullptr), level_(level), domain_(std::move(domain)), format_(format_flags), buf_(this)
|
|
{
|
|
set_enabled(true); // sets ostream's rdbuf
|
|
}
|
|
|
|
// DebugOutStream() : std::ostream(nullptr), buf_(this)
|
|
// {
|
|
// set_enabled(false);
|
|
// }
|
|
|
|
/// Construct with settings from another DebugOutStream.
|
|
DebugOutStream(const DebugOutStream& other, std::string domain)
|
|
: std::ostream(nullptr), level_(other.level_), domain_(std::move(domain)), format_(other.format_), buf_(this)
|
|
{
|
|
set_enabled(other.get_enabled()); // sets ostream's rdbuf
|
|
for (const auto& channel : other.channels_) {
|
|
channels_.push_back(channel);
|
|
}
|
|
}
|
|
|
|
/*
|
|
void set_level(debug_level::flag level)
|
|
{
|
|
level_ = level;
|
|
}
|
|
|
|
void set_domain(const std::string& domain)
|
|
{
|
|
domain_ = domain;
|
|
}
|
|
*/
|
|
|
|
/// Set format flags
|
|
void set_format(const debug_format::flags& format_flags)
|
|
{
|
|
format_ = format_flags;
|
|
}
|
|
|
|
/// Get format flags
|
|
[[nodiscard]] debug_format::flags get_format() const
|
|
{
|
|
return format_;
|
|
}
|
|
|
|
|
|
/// Enable or disable output. If disabled, any data sent to this
|
|
/// stream is discarded.
|
|
void set_enabled(bool enabled)
|
|
{
|
|
if (enabled) {
|
|
rdbuf(&buf_);
|
|
} else {
|
|
rdbuf(&get_null_streambuf());
|
|
}
|
|
}
|
|
|
|
/// Check whether the stream is enabled or not.
|
|
[[nodiscard]] bool get_enabled() const
|
|
{
|
|
return (rdbuf() == &buf_);
|
|
}
|
|
|
|
|
|
/// Set channel list to send the data to.
|
|
void set_channels(std::vector<DebugChannelBasePtr> channels)
|
|
{
|
|
channels_ = std::move(channels);
|
|
}
|
|
|
|
/// Get channel list
|
|
std::vector<DebugChannelBasePtr>& get_channels()
|
|
{
|
|
return channels_;
|
|
}
|
|
|
|
/// Add a channel to channel list.
|
|
/// This will claim the ownership of the passed parameter.
|
|
void add_channel(DebugChannelBasePtr channel)
|
|
{
|
|
channels_.push_back(channel);
|
|
}
|
|
|
|
|
|
/// Check if the last sent output is still on the same line
|
|
/// as the first one.
|
|
[[nodiscard]] bool get_is_first_line() const
|
|
{
|
|
return is_first_line_;
|
|
}
|
|
|
|
/// Set whether we're on the first line of the output or not.
|
|
void set_is_first_line(bool b)
|
|
{
|
|
is_first_line_ = b;
|
|
}
|
|
|
|
|
|
/// Force output of buf_'s contents to the channels.
|
|
/// This also outputs a prefix if needed.
|
|
std::ostream& force_output()
|
|
{
|
|
buf_.force_output();
|
|
return *this;
|
|
}
|
|
|
|
|
|
private:
|
|
|
|
debug_level::flag level_ = debug_level::dump; ///< Debug level of this stream
|
|
std::string domain_; ///< Domain of this stream
|
|
debug_format::flags format_; ///< Format flags
|
|
|
|
bool is_first_line_ = true; ///< Whether it's the first line of output or not
|
|
|
|
std::vector<DebugChannelBasePtr> channels_; ///< Channels that the output is sent to
|
|
|
|
DebugStreamBuf buf_; /// Streambuf for implementation.
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // ns debug_internal
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
/// @}
|