如何从C++程序中打开自定义I/O流?
众所周知,有三个默认I/O流映射到标准库中的预定义对象: 0:std::istream std::cin 1:std::ostream std::cout 2:std::ostream std::cerr和std::ostream std::clog 但是,您可以从bash脚本中创建额外的流3、4、 所以,您能用描述符3创建一个额外的输出流并将其绑定到std::ostream自定义对象吗?如果是,怎么做?std::ofstream没有做到这一点,因为它会创建一个名为3的文件,而这不是我想要的如何从C++程序中打开自定义I/O流?,c++,io,stream,posix,file-descriptor,C++,Io,Stream,Posix,File Descriptor,众所周知,有三个默认I/O流映射到标准库中的预定义对象: 0:std::istream std::cin 1:std::ostream std::cout 2:std::ostream std::cerr和std::ostream std::clog 但是,您可以从bash脚本中创建额外的流3、4、 所以,您能用描述符3创建一个额外的输出流并将其绑定到std::ostream自定义对象吗?如果是,怎么做?std::ofstream没有做到这一点,因为它会创建一个名为3的文件,而这不是我想要的 编
编辑:它不必是便携式的。如果它在POSIX上工作就足够了。如果您需要程序的可移植性,则不可能。C++ 11标准没有指定统一的方式。 但是,您可以定义自己的输出流缓冲区,该缓冲区覆盖溢出和xsputn虚拟函数,并使用系统特定的API使用指定的描述符将每个字符或字符序列写入流 大致如下:
class my_ostream_buf : public std::streambuf
{
public:
my_ostream_buf(int fd) : _fd(fd) { }
protected:
virtual int_type overflow (int_type c)
{
if (c != EOF)
{
char ch = c;
if (write(_fd, &ch, 1) != 1)
{
return EOF;
}
}
return c;
}
// This is not strictly necessary, but performance is better if you
// write a sequence of characters all at once rather than writing
// each individual character through a separate system call.
virtual std::streamsize xsputn(const char* s, std::streamsize num)
{
return write(_fd, s, num);
}
private:
int _fd = 0;
};
以下是您将如何使用它:
using namespace std;
int main()
{
int fd = ...; // Any file descriptor
my_ostream_buf buf(fd);
ostream os(&buf); // Take care of the lifetime of `buf` here, or create your
// own class that derives from ostream and encapsulates an
// object of type my_ostream_buf
os << "Hello" << endl;
}
如果你需要你的程序是可移植的,这是不可能的。C++ 11标准没有指定统一的方式。 但是,您可以定义自己的输出流缓冲区,该缓冲区覆盖溢出和xsputn虚拟函数,并使用系统特定的API使用指定的描述符将每个字符或字符序列写入流 大致如下:
class my_ostream_buf : public std::streambuf
{
public:
my_ostream_buf(int fd) : _fd(fd) { }
protected:
virtual int_type overflow (int_type c)
{
if (c != EOF)
{
char ch = c;
if (write(_fd, &ch, 1) != 1)
{
return EOF;
}
}
return c;
}
// This is not strictly necessary, but performance is better if you
// write a sequence of characters all at once rather than writing
// each individual character through a separate system call.
virtual std::streamsize xsputn(const char* s, std::streamsize num)
{
return write(_fd, s, num);
}
private:
int _fd = 0;
};
以下是您将如何使用它:
using namespace std;
int main()
{
int fd = ...; // Any file descriptor
my_ostream_buf buf(fd);
ostream os(&buf); // Take care of the lifetime of `buf` here, or create your
// own class that derives from ostream and encapsulates an
// object of type my_ostream_buf
os << "Hello" << endl;
}
标准中对此没有任何规定。以良好的状态 在IOStream的实现中,应该有一些额外的, std::filebuf的特定于实现的构造函数 取一个系统文件描述符,其类型取决于 系统,并从中创建一个filebuf。如果没有,你就得 创建自己的streambuf。这可能或多或少有点困难, 取决于你需要什么:如果你只需要一个简单的, 单向流读取或写入,但不能同时读取或写入,无 支持搜索,输入时无代码翻译,非常方便 相对简单。但你仍然必须熟悉 系统级请求,如读或写。如果你想 支持filebuf所做的一切,它比 复杂 编辑: 我只是想加一个例子。既然你提到bash, 我想:
class FdStreambuf : public std::streambuf
{
int myFd;
char buffer[1024];
bool writeBuffer()
{
int len = pptr() - pbase();
return len == 0 || write( myFd, pptr(), len ) == len;
}
protected:
int overflow( int ch )
{
int results = ch == traits::eof() ? 0 : ch;
if ( pbase() != NULL ) {
if ( ! writeBuffer() ) {
results = traits::eof();
}
}
setp( buffer, buffer + sizeof( buffer ) );
sputc( ch );
return ch;
}
int sync()
{
return writeBuffer() ? 0 : -1;
}
public:
FdStreambuf( int fd ) : myFd( fd ) {}
int close()
{
sync();
return ::close( myFd );
}
};
class FdOStream : private FdStreambuf, public std::ostream
{
public:
FdOStream( int fd )
: FdStreambuf( fd )
, std::ostream( this )
{
}
void close()
{
if ( FdStreambuf::close() != 0 ) {
setstate( std::ios_base::badbit );
}
}
};
我认为这是所有必要的,但有可能我
忘记了什么。标准中对此没有任何规定。以良好的状态 在IOStream的实现中,应该有一些额外的, std::filebuf的特定于实现的构造函数 取一个系统文件描述符,其类型取决于 系统,并从中创建一个filebuf。如果没有,你就得 创建自己的streambuf。这可能或多或少有点困难, 取决于你需要什么:如果你只需要一个简单的, 单向流读取或写入,但不能同时读取或写入,无 支持搜索,输入时无代码翻译,非常方便 相对简单。但你仍然必须熟悉 系统级请求,如读或写。如果你想 支持filebuf所做的一切,它比 复杂 编辑: 我只是想加一个例子。既然你提到bash, 我想:
class FdStreambuf : public std::streambuf
{
int myFd;
char buffer[1024];
bool writeBuffer()
{
int len = pptr() - pbase();
return len == 0 || write( myFd, pptr(), len ) == len;
}
protected:
int overflow( int ch )
{
int results = ch == traits::eof() ? 0 : ch;
if ( pbase() != NULL ) {
if ( ! writeBuffer() ) {
results = traits::eof();
}
}
setp( buffer, buffer + sizeof( buffer ) );
sputc( ch );
return ch;
}
int sync()
{
return writeBuffer() ? 0 : -1;
}
public:
FdStreambuf( int fd ) : myFd( fd ) {}
int close()
{
sync();
return ::close( myFd );
}
};
class FdOStream : private FdStreambuf, public std::ostream
{
public:
FdOStream( int fd )
: FdStreambuf( fd )
, std::ostream( this )
{
}
void close()
{
if ( FdStreambuf::close() != 0 ) {
setstate( std::ios_base::badbit );
}
}
};
我认为这是所有必要的,但有可能我
忘了什么。我把“和”的答案结合起来,这就是我得到的答案,以防有人需要它
溪流
我结合了“和”的答案,这是我得到的,以防有人需要
溪流
或者重写下溢和写入,因为他提到了一个额外的输出流。您并不真正需要xsgetn。我想加一点缓冲,这也不难。@JamesKanze:是的,缓冲会更好。我只是尽量让它简单。感谢示例实现,我将试一试。请看我的编辑,它不必在POSIX linux和mac之外进行移植,实际上。。。。我的_ostream_buf不应该公开继承std::streambuf吗?或者我错过了什么吗?@bitmask:好的,那么这个简单化的版本应该可以工作了。请记住,它不做任何缓冲。如果您需要缓冲,请查看James Kanze的示例。或者重写underflow并写入,因为他提到了额外的输出流。您并不真正需要xsgetn。我想加一点缓冲,这也不难。@JamesKanze:是的,缓冲会更好。我只是尽量让它简单。感谢示例实现,我将试一试。请看我的编辑,它不必在POSIX linux和mac之外进行移植,实际上。。。。我的_ostream_buf不应该公开继承std::streambuf吗?或者我错过了什么吗?@bitmask:好的,那么这个简单化的版本应该可以工作了。请记住,它不做任何缓冲。如果您需要缓冲,请查看James Kanze的示例。您是对的,ostream包装器可能很方便。怎样
无论如何,FdStreambuf继承可能应该是私有成员,这样包装器就可以是一个模板类,使其更通用。@bitmask您正在将指向streambuf的指针传递到istream的构造函数中。我希望streambuf在istream得到它之前被构造。基类是在成员之前按从左到右的顺序构造的,所以我在从istrea继承之前从streambuf私下继承。看见唯一的问题是,这涉及到不必要的内存分配,但是呃,你是对的,ostream包装器可能很方便。但是,FdStreambuf继承可能应该是一个私有成员,因此包装器可以是一个模板类,使其更通用。@位掩码您正在将指向streambuf的指针传递到istream的构造函数中。我希望streambuf在istream得到它之前被构造。基类是在成员之前按从左到右的顺序构造的,所以我在从istrea继承之前从streambuf私下继承。看见唯一的问题是,有一个不必要的内存分配涉及,但嗯。这对我工作!util::FdStreamBuf WritePipeBuf pipefd;std::ostream ChildDebug&writepebuf;这对我有用!util::FdStreamBuf WritePipeBuf pipefd;std::ostream ChildDebug&writepebuf;儿童调试