原始问题是:如何在换行后自动将某些内容插入到流中。只有在事后将某些内容(手动)插入到流中时才会发生插入。
下面是更详细的解释。
对于练习我正在编写自己的记录器类。此片段给出了基本的日志记录功能。
class Logger {
public:
static std::ostream & log( LogLevels::LogLevel level );
};
// Use it:
Logger::log( LogLevels::WARNING ) << "Something serious happened!" << std::endl;
该示例打印类似
的内容[ WARNING: 0 ] Something serious happened!
我想扩展此功能,以便在将新行插入流后,所有调试消息都会按日志标头的宽度缩进,直到再次调用Logger::log
为止。最好通过示例来解释:
// Example (1)
Logger::log( LogLevels::WARNING ) << "Something happened!"
<< std::endl
<< "Some More information: "
<< 42
<< " (Still on the same line)"
<< std::endl;
Logger::log( LogLevels::INFO ) << "Merely a status code"
<< std::endl
<< "Which reads: "
<< 21
<< " (Also on the same line)"
<< std::endl;
// Example (2)
std::ostream & os = Logger::log( LogLevels::WARNING );
os << "First line"
<< std::endl;
os << "Second line"
<< std::endl;
// Example (3)
// [...]
// Some code is executed
Logger::log( LogLevels::WARNING ) << "A new error"
<< std::endl
<< "The code strikes back"
<< std::endl
<< "The return of the bugs"
<< std::endl ;
哪会产生:
[ WARNING: 0 ] Something hapened! Some More information: 42 (Still on the same line) [ INFO: 1 ] Merely a status code Which reads: 21 (Also on the same line) [ WARNING: 2 ] First line Second line // [...] [ WARNING: 99998 ] A new error The code strikes back The return of the bugs
是否可以实现此行为?如果是,如何实现?
答案 0 :(得分:2)
只需在ostream和ostream之间插入过滤streambuf 最终目标streambuf。像下面这样的东西应该做 诀窍:
class HeaderInserter : public std::streambuf
{
std::streambuf* myDest;
bool myIsAtStartOfLine;
protected:
int overflow( int ch ) override
{
int retval = 0;
if ( ch != traits_type::eof() ) {
if ( myIsAtStartOfLine ) {
std::string header = getHeader();
myDest->sputn( header.data(), header.size() );
}
retval = myDest->sputc( ch );
myIsAtStartOfLine = ch == '\n';
}
return retval;
}
public:
HeaderInserter( std::streambuf* dest )
: myDest( dest )
, myIsAtStartOfLine( true )
{
}
};
使用指向最终目的地的指针创建其中一个
(std::cerr.rdbuf()
或您已打开的std::filebuf
,
然后使用指向它的std::ostream
。
我在自己的记录器类中使用它;我添加了其他功能
开始和完成一个新的记录器记录:之后的第一个输出
开始记录输出时间戳,加上__FILE__
和。{
我传下来的__LINE__
;以下标题是
只需一个任意数量的空白区域即可缩进。而且
完成功能还将确保记录结束
一个'\n'
,然后刷新。
最后,一些更一般的评论:首先,你不想要
客户端代码调用Logger::log
,而不是一些宏
为了自动将__FILE__
和__LINE__
传递给。{
创建实际对象。对象本身应该是
一个临时的,其析构函数调用上面的完成例程。
最后,如果您处于多线程环境中,那么您将会这样做
可能想要实际定位streambuf
无限缓冲区(例如std::vector<char>
),其中
忽略用户的任何刷新,然后执行任何操作
完成时以原子方式写入整个缓冲区所必需的
调用例程,当然确保每个线程都有它
所有上述组件的自己的实例。 (如果你在
例如,一个Unix平台,Posix函数write
是
保证原子,所以你不需要任何锁。)
答案 1 :(得分:1)
我认为更直观的界面是:
Logger::log( LogLevels::WARNING ) << "Something happened!"
<< Logger::tabbedLine
<< "More Info";
因此,用户仍然可以使用'\n'
或std::endl
来到下一行的开头,或使用Logger::tabbedLine
(一个坏名字,我承认)来实现你的目标想。
您可以继承std::ostream
并为operator<<
添加一个重载,它接收一个特殊的类tabbedLine
是此类型的静态实例。您将需要“记住”您创建的此子流子类中所需的填充。
Logger::log
将返回此ostream
子类的实例。
答案 2 :(得分:1)
您可以编写自己的流,流缓冲区和操纵器:
#include <iostream>
#include <iomanip>
#include <sstream>
// LogBuffer
// ============================================================================
class LogBuffer : public std::streambuf
{
// Types
// =====
public:
typedef typename std::streambuf buffer_type;
typedef typename buffer_type::char_type char_type;
typedef typename buffer_type::traits_type traits_type;
typedef typename buffer_type::int_type int_type;
typedef typename buffer_type::pos_type pos_type;
typedef typename buffer_type::off_type off_type;
// Construction/Destructiion
// =========================
public:
LogBuffer(buffer_type& buffer)
: m_buffer(buffer), m_indent(0), m_warning_count(0)
{}
public:
~LogBuffer() {
m_buffer.pubsync();
}
private:
LogBuffer(LogBuffer const&); // No Copy.
LogBuffer& operator=(LogBuffer const&); // No Copy.
// Functionality
// =============
public:
bool write(const std::string& s) {
return m_buffer.sputn(s.data(), s.size()) == std::streamsize(s.size());
}
bool warning() {
std::ostringstream out;
out << "[ WARNING: " << m_warning_count++ << "] ";
m_indent = out.str().size();
return write(out.str());
}
bool insert_indent() {
std::ostringstream out;
out << std::setw(m_indent) << "";
return write(out.str());
}
// Virtuell
// ========
protected:
std::streamsize xsputn(const char_type* s, std::streamsize n) {
return m_buffer.sputn(s, n);
}
int_type overflow(int_type ch) {
if(ch == traits_type::eof()) return traits_type::eof();
else return m_buffer.sputc(traits_type::to_char_type(ch));
}
int sync() {
return m_buffer.pubsync();
}
private:
buffer_type& m_buffer;
unsigned m_indent;
unsigned m_warning_count;
};
// LogStream
// ============================================================================
class LogStream : public std::ostream
{
// Types
// =====
private:
typedef std::ostream Base;
typedef LogBuffer buffer_type;
public:
typedef std::ostream stream_type;
typedef typename Base::char_type char_type;
typedef typename Base::traits_type traits_type;
typedef typename Base::int_type int_type;
typedef typename Base::pos_type pos_type;
typedef typename Base::off_type off_type;
// Construction
// ============
public:
LogStream()
: Base(&m_buffer), m_buffer(*std::clog.rdbuf())
{}
LogStream(stream_type& stream)
: Base(&m_buffer), m_buffer(*stream.rdbuf())
{}
private:
LogStream(const LogStream&); // No copy.
const LogStream& operator = (const LogStream&); // No copy.
private:
buffer_type m_buffer;
};
// Manipulator
// ===========
std::ostream& log_warning(std::ostream& stream) {
LogBuffer* buffer = dynamic_cast<LogBuffer*>(stream.rdbuf());
if(buffer) {
if( ! buffer->warning())
stream.setstate(std::ios_base::failbit);
}
return stream;
}
std::ostream& log_indent(std::ostream& stream) {
LogBuffer* buffer = dynamic_cast<LogBuffer*>(stream.rdbuf());
if(buffer) {
if( ! buffer->insert_indent())
stream.setstate(std::ios_base::failbit);
}
return stream;
}
int main() {
LogStream log;
log << log_warning
<< "First\n"
<< log_indent
<< "Second\n"
<< std::flush;
}