如何从C++程序中打开自定义I/O流

How to open custom I/O streams from within a C++ program?

本文关键字:自定义 C++ 程序      更新时间:2023-10-16

众所周知,有三个默认I/O流映射到标准库中的预定义对象:

  • 0:std::istream std::cin
  • 1:std::ostream std::cout
  • 2:std::ostream std::cerrstd::ostream std::clog

然而,从(例如)bash脚本中,您可以创建额外的流(3,4,…)

那么,您能用描述符3创建一个额外的输出流并将其绑定到std::ostream custom对象吗?如果是,如何?std::ofstream不起作用,因为它会创建一个名为"3"的文件,这不是我想要的。


编辑:它不一定是可移植的。如果它在POSIX上工作就足够了。

如果您需要程序的可移植性,这是不可能的。C++11标准没有规定统一的方法。

但是,您可以定义自己的输出流缓冲区,该缓冲区覆盖overflow()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。这可能或多或少是困难的,取决于你需要什么:如果你只需要一个简单的,单向流(读或写,但不能同时读和写),无支持搜索,无需输入代码翻译相对简单。(但你仍然需要熟悉系统级请求,如readwrite。)如果你想支持filebuf所做的一切复杂的

编辑:

我只是想加一个例子。既然你提到CCD_ 13,我假设Unix:

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 );
        }
    }
};

(我认为这是所有必要的,但我有可能忘记了什么。)

我结合了Andy和James的答案,这就是我得到的(以防有人需要)

Streams.h

#pragma once
#include <ostream>
#include <unistd.h>
namespace util {
  class StreamWrapperImpl : public std::ostream {
    private:
      typedef std::streambuf* OwnedBufPtr;
      OwnedBufPtr const buf;
    public:
      StreamWrapperImpl(OwnedBufPtr buf)
      : std::ostream(buf)
      , buf(buf)
      {}
      virtual ~StreamWrapperImpl() {
        delete buf;
      }
  };
  template <typename Buf>
  class StreamWrapper : public StreamWrapperImpl {
    public:
      StreamWrapper()
      : StreamWrapperImpl(new Buf())
      {}
      template <typename Arg>
      StreamWrapper(Arg arg) // this could use some perfect forwarding in C++11
      : StreamWrapperImpl(new Buf(arg))
      {}
  };
  class FdStreamBuf : public std::streambuf {
    private:
      int fd;
    protected:
      virtual int_type overflow(int_type c) {
        if (c != EOF) {
          char const ch = c;
          if (write(fd, &ch, 1) != 1)
            return EOF;
        }
        return c;
      }
      virtual std::streamsize xsputn(char const* s, std::streamsize num) {
        return write(fd, s, num);
      }
    public:
      FdStreamBuf(int fd)
      : fd(fd)
      {
      }
  };
}