It is straight forward to create a stream buffer using a FILE*
as underlying destination and create a corresponding std::ostream
using such a stream buffer. It would roughly look something like this:
#include <stdio.h>
#include <streambuf>
#include <ostream>
class stdiobuf
: public std::streambuf {
enum { bufsize = 2048 };
char buffer[bufsize];
FILE* fp;
int (*close)(FILE*);
int overflow(int c) {
if (c != std::char_traits<char>::eof()) {
*this->pptr() = std::char_traits<char>::to_char_type(c);
this->pbump(1);
}
return this->sync()
? std::char_traits<char>::eof()
: std::char_traits<char>::not_eof(c);
}
int sync() {
std::streamsize size(this->pptr() - this->pbase());
std::streamsize done(this->fp? fwrite(this->pbase(), 1, size, this->fp): 0);
this->setp(this->pbase(), this->epptr());
return size == done? 0: -1;
}
public:
stdiobuf(FILE* fp, int(*close)(FILE*) = fclose)
: fp(fp)
, close(close) {
this->setp(this->buffer, this->buffer + (this->fp? bufsize - 1: 0));
}
~stdiobuf() {
this->sync();
this->fp && this->close(this->fp);
}
};
class opipestream
: private virtual stdiobuf
, public std::ostream {
public:
opipestream(std::string const& pipe)
: stdiobuf(popen(pipe.c_str(), "w"), pclose)
, std::ios(static_cast<std::streambuf*>(this))
, std::ostream(static_cast<std::streambuf*>(this)) {
}
};
int main()
{
opipestream out("/usr/bin/sed -e 's/^/xxxx /'");
out << "Hello\n";
out << "world\n";
}
The basic idea is that you can create a new stream by implementing a stream buffer. The implementation above should be fairly complete. The error handling when an incomplete buffer could be improved although the most likely case of an error is that the pipe was closed and there isn't really much what could be done.
boost::iostreams::file_descriptor
. It has very bad docs, but it can help you and it is cross-platform. – Goodwillostream
– DorenefileStream
before creating the process, then feed that as input tomy_prog
, or is some of the input not known until after the process is created? – Indistinctostream
s. – Clytestd::ostream
? I am not sure about complexity but simple not-so-optimized version, as I guess, could be completed in one day. What I am sure about is that you shouldn't just writepopen
in your code: use RAII class withpopen
in constructor andpclose
in destructor. – Shrubstd::basic_ostream
?std::ostream
is not meant to be inherited from; allstd::basic_
* classes are. – Gauvinstd::streambuf
that can be passed to the constructor ofstd::ostream
. A short web search brought up this site which seems to already have code forFILE*
that could easily be adjusted to work cross platform. – Maudemaudie