2

众所周知,有三个默认 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 上工作就足够了。

4

3 回答 3

2

如果您需要您的程序是可移植的,这是不可能的。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;
}
于 2013-02-06T16:39:06.493 回答
1

There's nothing provided for this in the standard. In a good implementation of IOStream, there should be some additional, implementation specific constructors for std::filebuf, which take a system file descripter (whose type will depend on the system), and create a filebuf from that. If not, you'll have to create your own streambuf. This can be more or less difficult, depending on what you need: if you're just need a simple, uni-directional stream (read or write, but not both), with no support for seeking and no code translation on input, it's relatively simple. (But you still have to be familiar with the system level requests, like read or write.) If you want to support everything that filebuf does, it is significantly more complicated.

EDIT:

Just thought I'd add an example. Since you speak of bash, I'll suppose 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 );
        }
    }
};

(I think that's all that's necessary, but it's possible I've forgotten something.)

于 2013-02-06T16:44:14.510 回答
1

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

流.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)
      {
      }
  };
}
于 2013-02-06T23:30:29.443 回答