views:

323

answers:

3

Suppose I "popen" an executable, I get a FILE* in return. Furthermore, suppose I'd like to "connect" this file to an istream object for easier processing, is there a way to do this?

+1  A: 

Sure there's a way, implement your own istream that can be constructed from a FILE*.

If you're asking whether there is a standard way to do this, then no.

Frank Krueger
+2  A: 

There is no standard way but if you want a quick solution you can get the file descriptor with fileno() and then use Josuttis' fdstream. There may be similar efforts around but I used this in the distant past and it worked fine. If nothing else it should be a very good map to implementing your own.

Duck
A: 

You can get away by deriving std::basic_streambuf or std::streambuf classes.
Something along these lines:

#include <stdio.h>
#include <iostream>

#define BUFFER_SIZE     1024

class popen_streambuf : public std::streambuf {
public:
    popen_streambuf() : fp(NULL) {
    }

    popen_streambuf *open(const char *command, const char *mode) {
        fp = popen(command, mode);
        if (NULL == fp) {
            return NULL;
        }

        buffer = new char_type[BUFFER_SIZE];
        setg(buffer, buffer, buffer);

        return this;
    }

    std::streamsize xsgetn(char_type *ptr, std::streamsize n) {
        std::streamsize got = showmanyc();
        if (n <= got) {
            memcpy(ptr, gptr(), n * sizeof(char_type));
            gbump(n);

            return n;
        }

        memcpy(ptr, gptr(), got * sizeof(char_type));
        gbump(got);

        if (traits_type::eof() == underflow()) {
            return got;
        }

        return (got + xsgetn(ptr + got, n - got));
    }

    int_type underflow() {
        if (gptr() == 0) {
            return traits_type::eof();
        }

        if (gptr() < egptr()) {
            return traits_type::to_int_type(*gptr());
        }

        size_t len = fread(eback(), sizeof(char_type), BUFFER_SIZE, fp);
        setg(eback(), eback(), eback() + (sizeof(char_type) * len));

        if (0 == len) {
            return traits_type::eof();
        }

        return traits_type::to_int_type(*gptr());
    }

    std::streamsize showmanyc() {
        if (gptr() == 0) {
           return 0;
        }

        if (gptr() < egptr()) {
            return egptr() - gptr();
        }

       return 0; 
    }

private:
    FILE *fp;
    char_type *buffer;
};

int main(int argc, char *argv)
{
    char c;
    popen_streambuf sb;
    std::istream is(&sb);

    if (NULL == sb.open("ls -la", "r")) {
        return 1;
    }

    while (is.read(&c, 1)) {
        std::cout << c;
    }

    return 0;
}
ihuk