Я хотел бы отправить данные из моей 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
,
Создать потоковый буфер с помощью 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";
}
Основная идея заключается в том, что вы можете создать новый поток, реализовав буфер потока. Реализация выше должна быть довольно полной. Обработка ошибок, когда неполный буфер может быть улучшен, хотя наиболее вероятным случаем ошибки является то, что канал был закрыт и на самом деле мало что можно сделать.
Мой старый ответ работал только под Windows из-за отсутствия std::filebuf
т е р. Как 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");
Вы можете использовать что-то другое, кроме popen здесь, возможно, прямой system
вызов. После того, как вы все сделали, вы хотите удалить input.txt
файл.