Why Are C++ Stl iOStreams Not "Exception Friendly"

Why are C++ STL iostreams not exception friendly?

  1. C++ wasn't built with exceptions from day one. "C with classes" started in 1979, and exceptions were added in 1989. Meanwhile, the streams library was written as early as 1984 (later becomes iostreams in 1989 (later reimplemented by GNU in 1991)), it just cannot use exception handling in the beginning.

    Ref:

    • Bjarne Stroustrup, A History of C++: 1979−1991
    • C++ Libraries
  2. You can enable exceptions with the .exceptions method.

// ios::exceptions
#include <iostream>
#include <fstream>
#include <string>

int main () {
std::ifstream file;
file.exceptions(ifstream::failbit | ifstream::badbit);
try {
file.open ("test.txt");
std::string buf;
while (std::getline(file, buf))
std::cout << "Read> " << buf << "\n";
}
catch (ifstream::failure& e) {
std::cout << "Exception opening/reading file\n";
}
}

Why does C++'s exception not provide the calling details?

The issue is the C++ object model, which differs from Python's. To contrast, first let's answer: what does Python store in the exception object to print the key? It's a reference that keeps that object alive.

This cannot be done simply in C++.

  1. std::out_of_range can't store a pointer or reference to the key as is. The handler for the exception may be in far away block. And that means that the key most probably went out of scope before the handler is entered. We get undefined behavior if the key is referred to.

  2. std::out_of_range is a concrete class. Not a template like std::map. It can't easily copy the key into itself. There are many different Key types, and it obviously can't account for all of them. Even if it could, what if the key is extremely expensive to copy? Or even non-copyable at all? Even in cases where that's not true, what if the key isn't convertible to a string, or printable?

The above points don't mean it's impossible. std::map::at can throw a sub-class of std::out_of_range that does type erasure and so forth. But I hope you see it has non-trivial overhead. C++ is all about not paying in performance for features you don't need or use. Making everyone bear that overhead unconditionally isn't in line with this design.

C++ ifstream exception masks for C#

Both FileStream.Read and FileStream.Write methods will throw an IOException should any underlying I/O error occur. This is default behaviour.

Unlike C++, exception throwing is the expected form of error handling in C#. Because exceptions were a part of .NET from the beginning, there is none of the contention surrounding use of exceptions with streams as there is with C++.

Most - if not all - of the .NET framework standard libraries will throw exceptions in exceptional states. The only exception I can think of is when using the Try-Parse pattern, which is pretty explicit about the expected behaviour.

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

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";
}

C++ fstream: throwing exception when reaching eof

The basic problem in your code is a FAQ. You should never use eof() as a test condition of a read loop because in C/C++ (unlike some other languages) eof() is not set to true until you have read past the end of file, and therefore the body of the loop will be entered once too many times.

The idiomatically correct procedure is to have the read operation itself in the loop condition, so that the exit occurs at the correct point:

  while ( input1.get(in1) && input2.get(in2) ) { /* etc */ }
// here, after the loop, you can test eof(), fail(), etc
// if you're really interested in why the loop ended.

This loop will end naturally with the exhaustion of the smaller input file, which is exactly what you want.

Is it safe to use cerr when handling bad_alloc?

Simple case

There is one failing big allocation -possibly due to programmer's mistake-

int main() 
{

try {
std::size_t bytesToAllocate;
std::cin >> bytesToAllocate;

std::unique_ptr<char> ptr { new char[bytesToAllocate - 1] };
// ops, if user enters 0 or extraction fails we get
// std::numeric_limits<std::size_t>::max() -1 bytes to allocate

} catch (const std::bad_alloc& e) {
std::cerr << "Failed to allocate memory.\n";
}
}

Here, even if new fails, we definitely have more memory because there was none used before.

Realistic case

If, for some unspecified reason, a character insertion fails, the internal failbit is enabled i.e setstate(std::ios_base::failbit) and, if exception is set for failbit, an exception is thrown. Moreover, if an exception is thrown during an insertion, badbit is set and, if exception is set on badbit, the exception is rethrown.

However, AFAIK, it is left uncovered and therefore unspecified whether such operation allocates memory or not and how it's done. Your program could be killed because of out-of-memory protections and so without having chance to catch the exception, if the whole process of exception propagating is possible at all in that condition.

Why doesn't C++ reimplement C standard functions with C++ elements/style?

Another more general question is why do not STL reimplementate all the standard C libraries

Because the old C libraries do the trick. The C++ standard library only re-implements existing functionality if they can do it significantly better than the old version. And for some parts of the C library, the benefit of writing new C++-implementations just isn't big enough to justify the extra standardization work.

As for atoi and the like, there are new versions of these in the C++ standard library, in the std::stringstream class.

To convert from a type T to a type U:

T in;
U out;
std::stringstream sstr(in);
sstr >> out;

As with the rest of the IOStream library, it's not perfect, it's pretty verbose, it's impressively slow and so on, but it works, and usually it is good enough. It can handle integers of all sizes, floating-point values, C and C++ strings and any other object which defines the operator <<.

EDIT:In addition,I have heard many other libraries avaliable for C++ make a lot of enhancement and extensions to STL.So does there libraries support these functions?

Boost has a boost::lexical_cast which wraps std::stringstream. With that function, you can write the above as:

U out = boost::lexical_cast<U>(in);

How should errors be handled when creating RAII classes?

The recommended course of action is to throw an exception on any failure that occurs during construction. Quoting the C++ FAQ:

Q. How can I handle a constructor that fails?

A. Throw an exception.

Constructors don’t have a return type, so it’s not possible to use return codes. The best way to signal constructor failure is therefore to throw an exception. If you don’t have the option of using exceptions, the “least bad” work-around is to put the object into a “zombie” state by setting an internal status bit so the object acts sort of like it’s dead even though it is technically still alive.

If construction involves RAII then the constructor has the additional responsibility to cleanup any already allocated resources prior to throwing.

Q. How should I handle resources if my constructors may throw exceptions?

A. Every data member inside your object should clean up its own mess.

If a constructor throws an exception, the object’s destructor is not run. If your object has already done something that needs to be undone (such as allocating some memory, opening a file, or locking a semaphore), this “stuff that needs to be undone” must be remembered by a data member inside the object.

As to why std::fstream constructors do not throw exceptions and use the fallback option by default (put the object into a “zombie” state), that is a combination of history and convenience, better explained in the answers to Why are C++ STL iostreams not “exception friendly”?.



Related Topics



Leave a reply



Submit