Как мне управлять::std::cout после изменения дескриптора файла 1, чтобы он ссылался на другой файл?

Я хотел бы сделать dup2(fd, 1); close(fd); и имеют ::std::cout пиши на новый фд 1. Как мне сбросить состояние ::std::cout так ничего смешного не получается? Например, достаточно ли промывки заранее? Или это еще не все?

Мне также интересно, то же самое с ::std::cin,

Существует ли стандартный механизм их сброса, если вы измените дескрипторы файлов, которые они используют под ними?

Чтобы быть ясным, моя цель здесь в основном перенаправить мой собственный ввод и вывод в другом месте. Я не хочу, чтобы процесс непреднамеренно срывал что-то на stdout своего родителя или пытался потреблять что-либо из stdin своего родителя. И я никогда не хочу больше трогать стандартный ввод или вывод моего родителя. Я хочу забыть, что они когда-либо существовали.

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

Моя цель состоит в том, чтобы привести cin и cout к совершенно другим местам, чем они делали, когда процесс начался, и никогда не касаться мест, где они раньше вели. Когда-либо!

2 ответа

Решение

Вариант 1: установить стандартный и стандартный вывод

По данным cppreference.com:

По умолчанию все восемь стандартных потоков C++ синхронизируются с соответствующими им потоками C.

И пока вы явно не звонили sync_with_stdio(false)они останутся такими. Что это значит? Следующие:

На практике это означает, что синхронизированные потоки C++ не буферизуются, и каждая операция ввода-вывода в потоке C++ немедленно применяется к соответствующему буферу потока C. Это позволяет свободно смешивать C++ и C I/O.

Так, flush()твой cin & cout до dup()Их должно быть достаточно, поскольку они должны быть в согласованном состоянии.

Если вы хотите работать с файлами, например, вы можете использовать:

if (freopen("input.txt", "r", stdin) == NULL) {
    // Handle error, errno is set to indicate error
}

if (freopen("output.txt", "w", stdout) == NULL) {
    // Handle error, errno is set to indicate error
}

Примечание 1: Настройка глобального extern FILE * stdin или же stdout не будет работать, потому что он просто изменяет один экземпляр указателя на соответствующий FILE структура ОС. Любой модуль, который скопировал этот указатель в любой момент до этого изменения, продолжит использовать старый FILE, Конкретным примером является реализация libC++ для coutкакие копии FILE * stdout частному члену во время инициализации объекта. freopen с другой стороны меняет внутренний FILE структура ОС для использования вновь открытого файла, затрагивающая любого, кто имеет FILE * к этому.

Примечание 2: при использовании dup() вкусы (а не freopen()), мы меняем базовый fd, а не FILE*, freopen() метод делает больше, чем это. Из POSIX:

Функция freopen() должна сначала попытаться очистить поток, связанный с потоком, как если бы он вызывал fflush(stream). Неспособность успешно очистить поток должна игнорироваться. Если pathname не является нулевым указателем, freopen() закрывает любой файловый дескриптор, связанный с потоком. Неспособность закрыть файловый дескриптор успешно игнорируется. Индикаторы ошибок и конца файла для потока должны быть очищены.

dup()-ing может работать, но это может быть сложно, так как это не повлияет на другие свойстваFILE*, в том числе: ширина символа, состояние буферизации, буфер, ввод-вывод, индикатор двоичного / текстового режима, индикатор состояния конца файла, индикатор состояния ошибки, индикатор положения файла и (после C++17) блокировка повторного входа, используемая для предотвращения данные гонки.

По возможности я предлагаю использовать freopen, В противном случае вы могли бы выполнить действия, описанные самостоятельно (fflush(), clearerr()). Пропуская fclose() будет мудрым, так как мы не сможем открыть тот же внутренний FILE любым из методов API.

Вариант 2: установите rdbuf для cin's & cout's ()

Наоборот, как и в некоторых комментариях, это замена cinи coutбазовый буфер с помощью rdbuf(),

Какие у вас варианты здесь?

  • Файловые потоки: Открыть ifstream & ofstream и использовать их:

    std::ifstream fin("input.txt");
    if (!fin) {
        // Handle error
    }
    cin.rdbuf(fin.rdbuf());
    
    std::ofstream fout("output.txt");
    if (!fout) {
        // Handle error
    }
    cout.rdbuf(fout.rdbuf());
    
  • Сетевые потоки: используйте буст boost::asio::ip::tcp::iostream (Это получено из std::streambuf и так будет работать)

    boost::asio::ip::tcp::iostream stream("www.boost.org", "http");
    if (!stream) {
        // Handle error
    }
    
    cin.rdbuf(stream.rdbuf());
    cout.rdbuf(stream.rdbuf());
    
    // GET request example
    
    cout << "GET /LICENSE_1_0.txt HTTP/1.0\r\n";
    cout << "Host: www.boost.org\r\n";
    cout << "Accept: */*\r\n";
    cout << "Connection: close\r\n\r\n";
    cout.flush();
    
    std::string response;
    std::getline(cin, response);
    
  • Пользовательские потоки: используйте свою собственную упаковку для std::streambuf, Смотрите пример здесь.

Вы можете создать (или использовать существующую) библиотеку для класса socket_streambuf и связать ее с std::cout/std::cin:

socket_streambuf<char> buffer{ "127.0.0.1:8888" }; // will call socket(), connect() or throw on failure
std::cout.rdbuf(&buffer); // re-direct cout to the network connection
std::cout << "Hello, World!\n"; // may call send() on basic_streambuf::overflow()

Таким образом, вам не придется беспокоиться о манипулировании состоянием (глобальных) буферов C-потока.

Другие вопросы по тегам