cytrinox
cytrinox

Reputation: 1946

Why does std::copy (from istream to ostream) raises an ios::failure exception?

The following code should copy data from an wifstream to wcout. After the content is copied, the program throws a ios::failure exception.

#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <locale>
#include <iterator>
#include <algorithm>


int main(void)
{
    std::locale::global(std::locale(""));

    std::wifstream is;
    is.exceptions( std::ios::failbit | std::ios::badbit );
    is.open("test.ts", std::ios::binary);

    is >> std::noskipws;

    std::istream_iterator<wchar_t, wchar_t> in(is);
    std::istream_iterator<wchar_t, wchar_t> end;

    std::copy(in, end,
              std::ostream_iterator<wchar_t, wchar_t>(std::wcout));

    return 0;
} 

The stream should only throw an exception (see exception mask) if anything goes bad, but not on EOF.

Upvotes: 8

Views: 3432

Answers (3)

Cubbi
Cubbi

Reputation: 47448

Because you're using std::istream_iterator, the attempt to read a character past the end of the stream sets both eofbit and failbit (and only after some error bits are set, does the iterator become equal to the end iterator)

Stripping to bare essentials and reverting to char to make it even simpler, program is equivalent to:

#include <iostream>
#include <fstream>
int main()
{
    std::ifstream is("test.txt", std::ios::binary);
    is.exceptions(std::ios::failbit); // failbit only because that's what you get
    is >> std::noskipws;
    if(is)
        for(char c; is >> c;) // will throw!
            std::cout << c;
}

Upvotes: 1

Jerry Coffin
Jerry Coffin

Reputation: 490398

According to §27.6.1.2.3/10:

After a sentry object is constructed a character is extracted from in, if one is available, and stored in c. Otherwise, the function calls in.setstate(failbit).

So, when it reaches the end of the file and can no longer extract a character, it will set the fail bit, which you've set to produce an exception. Using std::copy doesn't change the behavior -- an istream_iterator reads via operator>>.

You can copy the file a bit more easily:

std::wifstream is("test.ts", std::ios::binary);
std::wcout << is.rdbuf();

Upvotes: 0

Loki Astari
Loki Astari

Reputation: 264601

To avoid skipping white space use the std::istreambuf_iterator

std::copy(std::istreambuf_iterator<wchar_t, wchar_t>(is),
          std::istreambuf_iterator<wchar_t, wchar_t>(),
          std::ostream_iterator<wchar_t, wchar_t>(std::wcout));

The exception:

The local may be using codecvt facet that is failing.
Try commenting out the locale line see what happens.

Have you tried to print what the exceptions is?

try
{
    // do work
}
catch(std::exception const& e)
{
    std::cout << e.what() << "\n";
}

Upvotes: 2

Related Questions