如何在 C++ 下概括 `clearerr()`?...

How would one generalise `clearerr()` under C++?…

TL;DR

我知道如果程序监听 EOF(例如 ^D)作为停止输入的标志,例如通过依赖像 while (std::cin) {...}, one needs to call cin.clear() before standard input can be read from again (readers who'd like to know more, see this table).

这样的条件

我最近learned that this is insufficient, and that the underlying C file descriptors, including stdin, .

由于clearerr()需要一个C风格的文件描述符,而C++主要操作std::basic_streambuf之类的(例如cin),我想概括一些代码(见下面)到 运行 clearerr() 在任何 streambuf 的关联 C 风格文件描述符上,即使它可能不是 stdin.

编辑(1&2):
我想知道 stdin 是否是唯一一个行为如此的文件描述符(需要 clearerr() 到 运行)...?
如果不是,那么下面的代码应该结束泛化问题(zkoza 在他们的回答中指出的想法)

正如 zkoza 中指出的那样,stdin 是唯一在逻辑上需要这种处理的文件描述符(即 clearerr() ).只需要检查给定的 C++ 流是否真的附加到 *std::cin.rdbuf()

std::istream theStream /* some stream with some underlying streambuf */

if (theStream.rdbuf() == std::cin.rdbuf())
    clearerr(stdin);

背景

我正在用 C++ 编写一个工具,我需要获取多行用户输入,两次.

我知道有多种方法可以获取多行输入(例如等待双换行符),但我想使用 EOF 作为用户完成操作的信号 —与 gpg -s-e.

时没有什么不同

经过多次咨询(here, here, and on cppreference.com),我决定使用...(我引用第三个):

[the] idiomatic C++ input loops such as [...]

while(std::getline(stream, string)){...}

因为这些依赖于 std::basic_ios::operator bool 来完成他们的工作,我 确保 cin.rdstate() 在第一和第二个用户输入指令 之间被清除(使用cin.clear()).

我的代码要点如下:

std::istream& getlines (std::basic_istream<char> &theStream,
                        std::vector<std::string> &stack) {
    std::ios::iostate current_mask (theStream.exceptions());
    theStream.exceptions(std::ios::badbit);
    std::string &_temp (*new std::string);
    while (theStream) {
        if (std::getline(theStream, _temp))
            stack.push_back(_temp);   // I'd really like the input broken...
                                      // ... into a stack of `\n`-terminated...
                                      // ... strings each time
    }

    // If `eofbit` is set, clear it
    // ... since std::basic_istream::operator bool needs `goodbit`
    if (theStream.eof())
        theStream.clear(theStream.rdstate()
                        & (std::ios::failbit | std::ios::badbit));
                        // Here the logical AND with
                        // ... (failbit OR badbit) unsets eofbit
    
    // std::getline sets failbit if nothing was extracted
    if (theStream.fail() && !stack.size()) {
        throw std::ios::failure("No input recieved!");
    }
    else if (theStream.fail() && stack.size()) {
        theStream.clear(theStream.rdstate() & std::ios::badbit);
        clearerr(stdin); //  the part which I want to generalise
    }
    
    delete &_temp;
    theStream.exceptions(current_mask);
    return theStream;
}

这就是你需要的:

#include <iostream>

int main()
{
    std::cin.sync_with_stdio(true);
    char c = '1', d = '1';
    std::cout << "Enter a char: \n";
    std::cin >> c;
    std::cout << (int)c << "\n";
    std::cout << std::cin.eof() << "\n";
    std::cin.clear();
    clearerr(stdin);
    std::cout << std::cin.eof() << "\n";

    std::cout << "Enter another char: \n";
    std::cin >> d;
    std::cout << (int)d << "\n";
    std::cout << std::cin.eof() << "\n";
}

之所以有效,是因为默认情况下,C++ 的 std::cin 与 C 的 stdin 绑定(因此,实际上不需要第一行)。您必须修改代码以检查流是否为 std::cin,如果是,则执行 clearerr(stdin);

编辑:

实际上,sync_with_stdio 只确保 C 和 C++ 接口之间的同步,但在内部它们在相同的文件描述符上工作,这可能就是为什么 clearerr(stdin); 无论接口是否绑定sync_with_stdio

EDIT2:这些能解决您的问题吗? Getting a FILE* from a std::fstream https://www.ginac.de/~kreckel/fileno/ ?