C++ ostream 是在一行的开头吗?
Is the C++ ostream at the start of a line?
在 C++ 中,如何检测我的 std::ostream os
是否在一行的开头,换句话说(我认为)最近写入 os
的内容是 os<<'\n'
或 os<<std::endl()
,否则尚未向 os
?
写入任何内容
乍一看,这听起来没有必要,因为我可以自己跟踪状态。但常见的情况如下,其中跟踪将涉及以非常不自然的方式更改可能从 try
块调用的每个 os<<thing
语句。
try {
do_something_which_writes_to(std::cout);
}
catch(const My_error&error) {
print_a_newline_if_necessary(std::cout);
std::cout<<error<<"\n";
}
(实际上,当然,我们想写 error
到 std::cerr
但这通常会与 std::cout
混在一起,除非其中一个被重定向,所以我们仍然想要在打印到 std::cerr
之前终止 std::cout
行。我特意简化了示例以避免这种干扰。)
您可能认为 os.tellp()
会是答案,但 tellp()
似乎只适用于 std::ofstream
。至少对我来说,std::cout.tellp()
总是returns-1
,说明不支持。
至少在我阅读的时候,您真正想要的不是在当前行中获得位置的能力。相反,您真正想要的是能够打印保证位于行首的内容——如果前一个字符是换行符,则为当前行(而且,我猜它是否也是一个回车符return), 但除此之外打印一个换行符,然后是任何后续内容。
这里有一些代码可以做到这一点:
#include <iostream>
class linebuf : public std::streambuf
{
std::streambuf* sbuf;
bool need_newline;
int sync() {
return sbuf->pubsync();
}
int overflow(int c) {
switch (c) {
case '\r':
case '\n': need_newline = false;
break;
case '\v':
if (need_newline) {
need_newline = false;
return sbuf->sputc('\n');
}
return c;
default:
need_newline = true;
break;
}
return sbuf->sputc(c);
}
public:
linebuf(std::streambuf* sbuf)
: sbuf(sbuf)
, need_newline(true)
{}
std::streambuf *buf() const { return sbuf; }
~linebuf() { sync(); }
};
class linestream : public std::ostream {
linebuf buf;
std::ostream &os;
public:
linestream(std::ostream& out)
: buf(out.rdbuf())
, std::ios(&buf)
, std::ostream(&buf)
, os(out)
{
out.rdbuf(&buf);
}
~linestream() { os.rdbuf(buf.buf()); }
};
void do_stuff() {
std::cout << "\vMore output\v";
}
int main() {
{
linestream temp(std::cout);
std::cout << "\noutput\n";
std::cout << "\voutput";
do_stuff();
std::cout << "\voutput\n";
std::cout << "\voutput\v";
}
std::cout << "\voutput\v";
}
因为它几乎从来没有用过,所以我劫持了垂直制表符 ('\v'
) 来表示特殊行为。
要使用它,您只需创建一个类型为 linestream
的临时对象(抱歉,我现在太累了,想不出一个好名字),向它传递一个 ostream 对象,它将获得新的\v
写入时的行为。当该临时对象超出范围时,流将恢复到其原始行为(我怀疑是否有人经常使用 \v
来关心,但谁知道也许有人关心 - 这主要只是清理的副作用无论如何在它自己之后)。
在任何情况下,当 do_stuff
被调用时,特殊行为仍然存在,因此它不仅仅是局部于创建局部 linestream
对象的函数,或类似的东西——一旦创建,特殊行为将一直有效,直到它被销毁。
还有一点:when/if 你混合了 cout
和 cerr
的输出,这没有多大帮助。特别是,两者都不会完全了解对方的状态。你可能非常需要一些挂钩到输出终端(或那个订单上的东西)才能处理这个问题,因为输出重定向通常由 OS 处理,所以在程序内部甚至没有办法猜测写入 cout
和 cerr
的数据是否会去同一个地方。
在 C++ 中,如何检测我的 std::ostream os
是否在一行的开头,换句话说(我认为)最近写入 os
的内容是 os<<'\n'
或 os<<std::endl()
,否则尚未向 os
?
乍一看,这听起来没有必要,因为我可以自己跟踪状态。但常见的情况如下,其中跟踪将涉及以非常不自然的方式更改可能从 try
块调用的每个 os<<thing
语句。
try {
do_something_which_writes_to(std::cout);
}
catch(const My_error&error) {
print_a_newline_if_necessary(std::cout);
std::cout<<error<<"\n";
}
(实际上,当然,我们想写 error
到 std::cerr
但这通常会与 std::cout
混在一起,除非其中一个被重定向,所以我们仍然想要在打印到 std::cerr
之前终止 std::cout
行。我特意简化了示例以避免这种干扰。)
您可能认为 os.tellp()
会是答案,但 tellp()
似乎只适用于 std::ofstream
。至少对我来说,std::cout.tellp()
总是returns-1
,说明不支持。
至少在我阅读的时候,您真正想要的不是在当前行中获得位置的能力。相反,您真正想要的是能够打印保证位于行首的内容——如果前一个字符是换行符,则为当前行(而且,我猜它是否也是一个回车符return), 但除此之外打印一个换行符,然后是任何后续内容。
这里有一些代码可以做到这一点:
#include <iostream>
class linebuf : public std::streambuf
{
std::streambuf* sbuf;
bool need_newline;
int sync() {
return sbuf->pubsync();
}
int overflow(int c) {
switch (c) {
case '\r':
case '\n': need_newline = false;
break;
case '\v':
if (need_newline) {
need_newline = false;
return sbuf->sputc('\n');
}
return c;
default:
need_newline = true;
break;
}
return sbuf->sputc(c);
}
public:
linebuf(std::streambuf* sbuf)
: sbuf(sbuf)
, need_newline(true)
{}
std::streambuf *buf() const { return sbuf; }
~linebuf() { sync(); }
};
class linestream : public std::ostream {
linebuf buf;
std::ostream &os;
public:
linestream(std::ostream& out)
: buf(out.rdbuf())
, std::ios(&buf)
, std::ostream(&buf)
, os(out)
{
out.rdbuf(&buf);
}
~linestream() { os.rdbuf(buf.buf()); }
};
void do_stuff() {
std::cout << "\vMore output\v";
}
int main() {
{
linestream temp(std::cout);
std::cout << "\noutput\n";
std::cout << "\voutput";
do_stuff();
std::cout << "\voutput\n";
std::cout << "\voutput\v";
}
std::cout << "\voutput\v";
}
因为它几乎从来没有用过,所以我劫持了垂直制表符 ('\v'
) 来表示特殊行为。
要使用它,您只需创建一个类型为 linestream
的临时对象(抱歉,我现在太累了,想不出一个好名字),向它传递一个 ostream 对象,它将获得新的\v
写入时的行为。当该临时对象超出范围时,流将恢复到其原始行为(我怀疑是否有人经常使用 \v
来关心,但谁知道也许有人关心 - 这主要只是清理的副作用无论如何在它自己之后)。
在任何情况下,当 do_stuff
被调用时,特殊行为仍然存在,因此它不仅仅是局部于创建局部 linestream
对象的函数,或类似的东西——一旦创建,特殊行为将一直有效,直到它被销毁。
还有一点:when/if 你混合了 cout
和 cerr
的输出,这没有多大帮助。特别是,两者都不会完全了解对方的状态。你可能非常需要一些挂钩到输出终端(或那个订单上的东西)才能处理这个问题,因为输出重定向通常由 OS 处理,所以在程序内部甚至没有办法猜测写入 cout
和 cerr
的数据是否会去同一个地方。