libc ++和libstdc ++之间的istream eof差异

时间:2012-01-25 14:53:57

标签: c++ istream libstdc++ libc++

以下(玩具)程序在与libstdc ++和libc ++链接时会返回不同的内容。这是libc ++中的错误还是我不明白istream eof()是如何工作的?我尝试在linux和mac os x上使用g ++并在mac os x上运行它,使用和不使用-std = c ++ 0x。我的印象是,在尝试读取(通过get()或其他东西)实际上失败之前,eof()不会返回true。这就是libstdc ++的行为方式,而不是libc ++的行为方式。

#include <iostream>
#include <sstream>

int main() {
    std::stringstream s;

    s << "a";

    std::cout << "EOF? " << (s.eof() ? "T" : "F") << std::endl;
    std::cout << "get: " << s.get() << std::endl;
    std::cout << "EOF? " << (s.eof() ? "T" : "F") << std::endl;

return 0;
}

Thor:~$ g++ test.cpp
Thor:~$ ./a.out
EOF? F
get: 97
EOF? F
Thor:~$ clang++ -std=c++0x -stdlib=libstdc++ test.cpp 
Thor:~$ ./a.out
EOF? F
get: 97
EOF? F
Thor:~$ clang++ -std=c++0x -stdlib=libc++ test.cpp 
Thor:~$ ./a.out
EOF? F
get: 97
EOF? T
Thor:~$ clang++ -stdlib=libc++ test.cpp 
Thor:~$ ./a.out
EOF? F
get: 97
EOF? T

4 个答案:

答案 0 :(得分:5)

编辑:这是由于旧版本的libc ++解释C ++标准的方式。在LWG issue 2036中讨论了这种解释,它被认为是不正确的,并且改变了libc ++。

当前的libc ++在测试中给出了与libstdc ++相同的结果。

老答案:

您的理解是正确的。

istream::get()执行以下操作:

  1. 调用good(),如果返回false则设置failbit(这会将一个failbit添加到其他位设置的流中),(§27.7.2.1.2[istream::sentry]/2
  2. 在必要时刷新任何领带(),
  3. 如果此时good()为false,则返回eof并且不执行任何其他操作。
  4. 通过调用rdbuf()->sbumpc()rdbuf()->sgetc()§27.7.2.1[istream]/2
  5. 提取 字符
  6. 如果sbumpc()sgetc()返回了eof,请设置eofbit。 (§27.7.2.1[istream]/3)和failbit§27.7.2.2.3[istream.unformatted]/4
  7. 如果抛出异常,请设置badbit(§27.7.2.2.3[istream.unformatted]/1)并在允许的情况下重新抛出。
  8. 更新gcount并返回字符(如果无法获取,则返回eof。)
  9. (引自C ++ 11的章节,但C ++ 03具有相同的规则,根据§27.6。*)

    现在让我们来看看实现:

    libc ++(当前的svn版本)将get()的相关部分定义为

    sentry __s(*this, true);
    if (__s)
    {
        __r = this->rdbuf()->sbumpc();
        if (traits_type::eq_int_type(__r, traits_type::eof()))
           this->setstate(ios_base::failbit | ios_base::eofbit);
        else
            __gc_ = 1;
    }
    

    libstdc ++(与gcc 4.6.2一起提供)定义与

    相同的部分
    sentry __cerb(*this, true);
    if (__cerb)
      {
        __try
          {
            __c = this->rdbuf()->sbumpc();
            // 27.6.1.1 paragraph 3
            if (!traits_type::eq_int_type(__c, __eof))
              _M_gcount = 1;
            else
              __err |= ios_base::eofbit;
          }
    [...]
    if (!_M_gcount)
      __err |= ios_base::failbit;
    

    正如您所看到的,当且仅当sbumpc()返回eof时,两个库都会调用sbumpc()并设置eofbit。

    您的测试用例使用两个库的最新版本为我生成相同的输出。

答案 1 :(得分:4)

这是一个libc ++错误,已经修复,正如Cubbi所说。我的错。详情如下:

http://lwg.github.io/issues/lwg-closed.html#2036

答案 2 :(得分:1)

s.eof()的值在第二次调用中未指定 - 它可能是 是真还是假,甚至可能都不一致。你只能说是 如果s.eof()返回true,则所有未来的输入都将失败(但如果它 返回false,不能保证将来的输入会成功)。 失败后(s.fail()),如果s.eof()返回true,则很可能(但是 不是100%确定)失败是由于文件结束。这是值得的 但是,考虑以下情况:

double test;
std::istringstream s1("");
s1 >> test;
std::cout << (s1.fail() ? "T" : "F") << (s1.eof() ? "T" : "F") << endl;
std::istringstream s2("1.e-");
s2 >> test;
std::cout << (s2.fail() ? "T" : "F") << (s2.eof() ? "T" : "F") << endl;

在我的机器上,两行都是"TT",尽管第一行是{{1}} 失败,因为没有数据(文件结束),第二个因为 浮点值格式不正确。

答案 3 :(得分:0)

当有一个尝试读取超过文件末尾的操作时设置eofbit,操作可能不会失败(如果你正在读取一个整数并且在整数之后没有行尾,我希望eofbit被设置但读取整数才能成功)。 I.E.我得到并期待FT

#include <iostream>
#include <sstream>

int main() {
    std::stringstream s("12");
    int i;
    s >> i;

    std::cout << (s.fail() ? "T" : "F") << (s.eof() ? "T" : "F") << std::endl;

    return 0;
}

这里我不希望istream :: get在返回的字符之后尝试读取(即我不希望它挂起,直到我输入下一行,如果我用它读取\ n),所以libstd ++似乎确实是对的,至少在QOI POV中。

istream :: get的标准描述只是说“提取一个字符c,如果一个可用”,而不描述如何以及似乎不会阻止libc ++行为。