在我们的项目中,我们使用对象模型中的 c++ 流运算符 (<<( 来打印出易于阅读的数据格式。一个简化的例子是这样的:
std::ostream& operator<<(std::ostream & oStream, const OwnClass& iOwnClass) {
oStream << "[SomeMember1: " << iOwnClass._ownMember1 << "]n";
oStream << "[SomeMember2: " << iOwnClass._ownMember2 << "]n";
}
导致日志记录:
[SomeMember1: foo]
[SomeMember2: bar]
我们现在想要的是能够缩进该运算符的结果。某些调用类可能不希望这样的结果,而是希望在每行之前添加 2 个空格缩进。我们可以向类添加一个成员来指定缩进,但这似乎不是一个优雅的解决方案。
当然,这不是一个很大的问题,但如果这可行,我们的日志记录会更好。
谢谢
最简单的解决方案是在 ostream
和实际的流。 像这样:
class IndentingOStreambuf : public std::streambuf
{
std::streambuf* myDest;
bool myIsAtStartOfLine;
std::string myIndent;
std::ostream* myOwner;
protected:
virtual int overflow( int ch )
{
if ( myIsAtStartOfLine && ch != 'n' ) {
myDest->sputn( myIndent.data(), myIndent.size() );
}
myIsAtStartOfLine = ch == 'n';
return myDest->sputc( ch );
}
public:
explicit IndentingOStreambuf(
std::streambuf* dest, int indent = 4 )
: myDest( dest )
, myIsAtStartOfLine( true )
, myIndent( indent, ' ' )
, myOwner( NULL )
{
}
explicit IndentingOStreambuf(
std::ostream& dest, int indent = 4 )
: myDest( dest.rdbuf() )
, myIsAtStartOfLine( true )
, myIndent( indent, ' ' )
, myOwner( &dest )
{
myOwner->rdbuf( this );
}
virtual ~IndentingOStreambuf()
{
if ( myOwner != NULL ) {
myOwner->rdbuf( myDest );
}
}
};
要插入,只需创建 streambif 的实例:
IndentingOStreambuf indent( std::cout );
// Indented output...
当indent
超出范围时,一切都会恢复正常。
(对于日志记录,我有一个稍微复杂的: LoggingOStreambuf
将__FILE__
和__LINE__
作为参数,集合 myIndent
到带有这些参数的格式化字符串,加上时间戳,在每个输出后将其重置为缩进字符串,收集std::ostringstream
中的所有输出,并以原子方式输出在析构函数中myDest
。
这可以使用自定义流操纵器来完成,该操作器将所需的缩进级别存储在流的内部可扩展数组的单词中。您可以使用函数请求这样的单词 ios_base::xalloc
.此功能将为您提供单词的索引。您可以使用 ios_base::iword
.实现它的一种方法是:
struct indent {
indent(int level) : level(level) {}
private:
friend std::ostream& operator<<(std::ostream& stream, const indent& val);
int level;
};
std::ostream& operator<<(std::ostream& stream, const indent& val) {
for(int i = 0; i < val.level; i++) {
stream << " ";
}
return stream;
}
std::ostream& operator<<(std::ostream & oStream, const OwnClass& iOwnClass) {
oStream << indent(oStream.iword(index)) << "[SomeMember1: " <<
iOwnClass._ownMember1 << "]n";
oStream << indent(oStream.iword(index)) << "[SomeMember2: " <<
iOwnClass._ownMember2 << "]n";
}
您必须弄清楚存储index
的位置。这有效地允许您将自定义状态添加到流中(请注意,这不是开箱即用的线程安全状态(。每个想要缩进的函数都应该将请求的缩进添加到流中,并在完成后再次减去它。您可以通过使用守卫来添加/减去所需的缩进来确保这种情况始终发生(恕我直言,这比使用操纵器更优雅(:
class indent_guard {
public:
indent_guard(int level, std::ostream& stream, int index)
: level(level),
stream(stream),
index(index)
{
stream.iword(index) += level;
}
~indent_guard() {
stream.iword(index) -= level;
}
private:
int level;
std::ostream& stream;
int index;
};
你可以像这样使用它:
void some_func() {
indent_guard(2, std::cout, index);
// all output inside this function will be indented by 2 spaces
some_func(); // recursive call - output will be indented by 4 spaces
// here it will be 2 spaces again
}
不太好的方法是添加一个全局变量,它告诉缩进。像这样:
std::string OwnClassIndentation;
std::ostream& operator<<(std::ostream & oStream, const OwnClass& iOwnClass) {
oStream << "[SomeMember1:" << OwnClassIndentation << iOwnClass._ownMember1 << "]n";
oStream << "[SomeMember2:" << OwnClassIndentation << iOwnClass._ownMember2 << "]n";
}
然后根据需要进行设置。
您可以创建自己的具有缩进变量的流类,并覆盖该类的 endl,插入缩进。