Отправка данных в программу через stdin и ostream. (С++)

Я хотел бы отправить данные из моей программы на C++ во внешний конвейер, например:

FILE* file = popen("my_prog -opt | other_prog", "w");
std::ostream fileStream = some_function(file);
fileStream << "some data";

Я понимаю, что нет простого кросс-платформенного способа сделать вторую строку, но есть ли способ выполнить то же самое, используя что-то другое, кроме popen? Мне не нужно использовать popen, но мне нужно использовать ostream. Его нужно будет скомпилировать с помощью clang и gcc как минимум, но желательно, чтобы он работал с любым компилятором. Я мог бы также изменить то, как я справляюсь с пронзительные, но у меня нет источника для my_prog или other_prog.

4 ответов


это прямо вперед, чтобы создать буфер потока с помощью FILE* в качестве базового назначения и создания соответствующего std::ostream использование такого буфера потока. Это будет примерно выглядеть примерно так:

#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";
}

основная идея заключается в том, что вы можете создать новый поток по реализации буфера потока. Вышеуказанное осуществление должно быть достаточно полным. Обработка ошибок, когда неполный буфер может быть улучшен, хотя наиболее вероятным случаем ошибки является то, что канал был закрыто, и на самом деле мало что можно сделать.


мой старый ответ работал только под окнами из-за отсутствия std::filebuf ctor. As user4815162342 указал, что альтернативой может быть использование __gnu_cxx::stdio_filebuf<char>.

я исправил что-то вместе, что теперь должно работать с windows и linux, но есть шанс, что ваши платформы могут не все работать.

#ifdef __GNUC__
    #include <ext/stdio_sync_filebuf.h>    
    typedef __gnu_cxx::stdio_sync_filebuf<char> popen_filebuf;
#elif _MSC_VER
    #include<fstream>
    typedef std::filebuf popen_filebuf;
    FILE*(*popen)(const char*, const char*) = _popen;
#else
    static_assert(false, "popen_filebuf is not available for this platform");
#endif

int main()
{
    FILE* file = popen("my_prog -opt | other_prog", "w");

    popen_filebuf buffer(file);
    std::ostream fileStream(&buffer);

    fileStream << "some data";

    return 0;
}

если вы заранее знаете, какие платформы вы поддерживаете, вы можете использовать расширения платформы для создания iostream из файлового дескриптора. Например, как показано здесь, GNU libstdc++ предоставляет __gnu_cxx::stdio_sync_filebuf который можно использовать для создания filebuf С FILE *. Класс, наследующий от std::iostream и инициализирует его соответствующим filebuf, протестировано с g++ 5.2 и clang++ 3.7 на Linux, может выглядеть так:

#include <iostream>
#include <ext/stdio_sync_filebuf.h>

class StdioStream:
  private __gnu_cxx::stdio_sync_filebuf<char>, public std::iostream {
public:
  explicit StdioStream(FILE *fp):
    __gnu_cxx::stdio_sync_filebuf<char>(fp), std::iostream(this) { }
};

int main()
{
    FILE* file = popen("my_prog -opt | other_prog", "w");
    StdioStream fileStream(file);
    fileStream << "some data";
}

обратите внимание, что нельзя просто определить some_function что возвращает std::ostream, потому что последний имеет собственный конструктор копирования, и потому, что средний filebuf необходимо уничтожить вместе с потоком. В приведенном выше примере используется множественное наследование для инициализации stdio_sync_filebuf (который в противном случае был бы член) перед iostream базовый класс - см. база-от членов для сведения.

ссылки ответ также содержит эквивалентный код для MSVC++ iostreams.


расширение моего комментария к исходному вопросу.

вы можете сохранить все входные данные в файл на диске, а затем передать этот файл в качестве входных данных в my_prog.

ofstream fileStream("input.txt");
fileStream << "some data";
// insert other input
fileStream.close();
FILE *file = popen("my_prog -opt <input.txt | other_prog", "w");

вы могли бы использовать что-то другое, чем попен здесь, возможно, прямой system звонок. Как только вы закончите, вы захотите удалить .