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

Я хотел бы отправить данные из моей 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,

9

Решение

Создать потоковый буфер с помощью 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";
}

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

7

Другие решения

Мой старый ответ работал только под 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;
}
3

Если вы заранее знаете, какие платформы вы поддерживаете, вы можете использовать специфичные для платформы расширения для создания 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.

3

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

Вы можете сохранить все входные данные в файл на диске, а затем передать этот файл в качестве входных данных для 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 файл.

0
По вопросам рекламы [email protected]