在我们的项目中,我们在对象模型中使用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个空格。我们可以在我们的类中添加一个指定缩进的成员,但这似乎不是一个优雅的解决方案。
当然这不是一个非常大的问题,但是如果这样做的话我们的日志会更好。
由于
答案 0 :(得分:24)
最简单的解决方案是在过滤器之间滑动过滤streambuf
ostream
和实际的streambuf。类似的东西:
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 );
}
}
};
要插入,只需创建streambuf的实例:
IndentingOStreambuf indent( std::cout );
// Indented output...
当indent
超出范围时,一切都恢复正常。
(对于日志记录,我有一个更复杂的:
LoggingOStreambuf
将__FILE__
和__LINE__
作为参数设置
myIndent
到带有这些参数的格式化字符串,加上一个时间
stamp,在每次输出后将其重置为缩进字符串,收集
std::ostringstream
中的所有输出,并以原子方式输出
析构函数中的myDest
。)
答案 1 :(得分:1)
不太好的方法是添加一个全局变量,它告诉缩进。像这样:
std::string OwnClassIndentation;
std::ostream& operator<<(std::ostream & oStream, const OwnClass& iOwnClass) {
oStream << "[SomeMember1:" << OwnClassIndentation << iOwnClass._ownMember1 << "]\n";
oStream << "[SomeMember2:" << OwnClassIndentation << iOwnClass._ownMember2 << "]\n";
}
然后根据需要进行设置。
答案 2 :(得分:1)
这可以使用自定义流操纵器来完成,该操纵器将所需的缩进级别存储在流的内部可扩展数组的单词中。您可以使用函数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
}
答案 3 :(得分:0)
您可以创建自己的具有缩进变量的流类,并覆盖该类的endl,插入缩进。