2

It is well known that there are three default I/O streams which are mapped to predefined objects in the standard library:

  • 0: std::istream std::cin
  • 1: std::ostream std::cout
  • 2: std::ostream std::cerr and std::ostream std::clog

However, from within (e.g.) a bash script you can create additional streams (3,4,...).

So, can you create an extra output stream with the descriptor 3 and bind it to an std::ostream custom object? If so, how? std::ofstream doesn't do the trick as it would create a file with the name "3" which is not what I want.


Edit: It doesn't have to be portable. It suffices if it works on POSIX.

bitmask
  • 32,434
  • 14
  • 99
  • 159

3 Answers3

2

It is not possible if you need your program to be portable. The C++ 11 Standard does not specify a unified way of doing that.

However, you can define your own output stream buffer which overrides the overflow() and xsputn() virtual functions and writes each character or sequence of characters to the stream with the specified descriptor using system-specific API.

Something along these lines:

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;    

};

And this is how you would use it:

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;
}
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Or overrides `underflow()`, and writes, since he spoke of an extra output stream. (You don't really need `xsgetn`.) I'd add a bit of buffering (which isn't difficult either). – James Kanze Feb 06 '13 at 16:46
  • @JamesKanze: Yes, buffering would be better. I just tried to keep it simple. – Andy Prowl Feb 06 '13 at 16:47
  • Thanks for the example implementation, I'll give it a shot. See my edit, it doesn't have to be portable beyond POSIX (linux and mac will do, actually). – bitmask Feb 06 '13 at 17:03
  • ... shouldn't `my_ostream_buf` inherit publicly from `std::streambuf` or did I miss something? – bitmask Feb 06 '13 at 17:06
  • @bitmask: Ok, then this simplistic version should work. Keep in mind that it does not do any buffering though. If you need buffering, check out James Kanze's example. – Andy Prowl Feb 06 '13 at 17:07
  • @bitmask: Yes, definitely it should derive from `std::streambuf`. I forgot that, sorry. Edited the answer. – Andy Prowl Feb 06 '13 at 17:09
  • @bitmask Buffering does complicate it a little bit more than first thought---with buffering, you have to worry about `sync`, and maybe add a `close` function (or at least `sync` in the destructor---in fact, in my code, I forgot the destructor for the streambuf, which should flush or close). – James Kanze Feb 06 '13 at 19:12
  • @JamesKanze: I don't want buffering anyway, so this should be fine. – bitmask Feb 06 '13 at 19:16
  • @bitmask You might still want to grab the ostream from my code. It's a more or less standard idiom, and once you get used to using custom streambuf... – James Kanze Feb 06 '13 at 19:25
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.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • You're right, the ostream wrapper could be handy. However, the FdStreambuf inheritance should probably be a private member instead --- so the wrapper could be a template class (making it more generic). – bitmask Feb 06 '13 at 19:28
  • @bitmask You're passing a pointer to the streambuf into the constructor of istream. I prefer that the streambuf be constructed _before_ istream gets it. Base classes are constructed before members, in left to right order, so I inherit privately from the streambuf before I inherit from istrea. – James Kanze Feb 06 '13 at 23:24
  • Yes, I noticed. See [my take on it](http://stackoverflow.com/a/14740883/430766). The only thing is that there is an unnecessary memory allocation involved, but ehhh. – bitmask Feb 06 '13 at 23:32
1

I combined Andy's and James's answer and this is what I got (in case somebody needs this)

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)
      {
      }
  };
}
Community
  • 1
  • 1
bitmask
  • 32,434
  • 14
  • 99
  • 159
  • This worked for me! util::FdStreamBuf WritePipeBuf( pipefd ); std::ostream ChildDebug(&WritePipeBuf); ChildDebug << "hello"; – Soylent Graham Feb 12 '15 at 14:22