【问题标题】:How to open custom I/O streams from within a C++ program? [duplicate]如何从 C++ 程序中打开自定义 I/O 流? [复制]
【发布时间】:2013-02-06 16:33:55
【问题描述】:

众所周知,有三个默认的 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++ io stream posix file-descriptor


    【解决方案1】:

    如果您需要您的程序是可移植的,这是不可能的。 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;
    }
    

    【讨论】:

    • 或覆盖underflow(),并写入,因为他谈到了额外的输出流。 (你真的不需要xsgetn。)我会添加一些缓冲(这也不难)。
    • @JamesKanze:是的,缓冲会更好。我只是尽量保持简单。
    • 感谢示例实现,我会试一试。请参阅我的编辑,它不必移植到 POSIX 之外(实际上,linux 和 mac 都可以)。
    • ...my_ostream_buf 不应该从std::streambuf 公开继承还是我错过了什么?
    • @bitmask:好的,那么这个简单的版本应该可以工作。请记住,它不会做任何缓冲。如果您需要缓冲,请查看 James Kanze 的示例。
    【解决方案2】:

    标准中对此没有任何规定。在一个好的 IOStream 的实现,应该还有一些额外的, std::filebuf 的实现特定构造函数,其中 获取系统文件描述符(其类型将取决于 system),并从中创建一个filebuf。如果没有,你将不得不 创建自己的流缓冲区。这可能或多或少有些困难, 取决于你需要什么:如果你只需要一个简单的, 单向流(读或写,但不是两者),没有 支持搜索并且没有输入代码翻译,它是 比较简单。 (但你还是要熟悉 系统级请求,例如 readwrite。)如果你想 支持filebuf 所做的一切,这要多得多 复杂。

    编辑:

    只是想我会添加一个示例。既然你说bash, 我假设是 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 );
            }
        }
    };
    

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

    【讨论】:

    • 你是对的,ostream 包装器可能很方便。但是,FdStreambuf 继承可能应该是私有成员 --- 因此包装器可以是模板类(使其更通用)。
    • @bitmask 您将指向streambuf 的指针传递给istream 的构造函数。我更喜欢 istream 得到它之前构造streambuf。基类在成员之前构建,按从左到右的顺序,所以我先从 streambuf 私有继承,然后再从 istrea 继承。
    • 是的,我注意到了。见my take on it。唯一的问题是,这涉及到不必要的内存分配,但是 ehhh。
    【解决方案3】:

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

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

    【讨论】:

    • 这对我有用! util::FdStreamBuf WritePipeBuf( pipefd ); std::ostream ChildDebug(&WritePipeBuf); ChildDebug
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-28
    • 2012-01-12
    • 2023-03-08
    • 1970-01-01
    • 2022-06-14
    • 1970-01-01
    • 2015-05-24
    相关资源
    最近更新 更多