5

Is it safe to use std::cerr like this?

try 
{
      Something();
} 
catch(std::bad_alloc) 
{
    cerr << "Out of memory!";
}

Does it use dynamic memory? If it fails, will it throw an exception or just not output anything?

edmz
  • 8,220
  • 2
  • 26
  • 45
gist
  • 63
  • 4
  • 2
    [Have a look at this answer and the necessary comments](http://stackoverflow.com/a/7749083/174614) – Tony The Lion Sep 21 '15 at 16:22
  • I think it is unlikely `cerr` would grab memory from the heap after its initialization at program startup. I am not sure anything guarantees it though. Its initialization is guaranteed and that is where heap resources are usually acquired. – Galik Sep 21 '15 at 16:45
  • I'm not aware of any line in the standard that guarantees `cerr <<` will never try to allocate memory from the heap, but it is guaranteed to do unbuffered output and flush certain other streams, so it's unlikely that a given implementation would. Test with a loop that gobbles up the heap one byte at a time and see if yours does? – Davislor Sep 21 '15 at 16:54
  • I just found that (according to the standard), even after initialization, `cerr` will allocate memory for an array the first time `std::basic_ios::copyfmt` is called. I suppose calling that at program start may make you a little safer...? – Galik Sep 21 '15 at 17:04
  • 1
    Gah according to http://en.cppreference.com/w/cpp/io/ios_base/iword that array can be reallocated at any time it seems. I think all bets are off. When disaster strikes, its a disaster. – Galik Sep 21 '15 at 17:17

2 Answers2

1

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.

edmz
  • 8,220
  • 2
  • 26
  • 45
  • So I understand correctly, the std::cerr is not safe because it can throw a new exception? Would wrapping the cerr in a second try catch that does nothing make it safe? The purpose is for diagnostics so it is tolerable if it fails to output. However the cerr should not compound the problem. – gist Sep 21 '15 at 22:44
  • `cerr` and other I/O objects throw if you tell them to. As said in the answer, it is not seem to be specified what happens in case of OOM, if `cerr` uses memory and how. – edmz Sep 22 '15 at 15:10
  • @gist Would you let the question open for some other time(leaving the solved mark then)? Someone can come up with a better answer, with concrete experiences maybe. – edmz Sep 22 '15 at 16:30
1

The standard(27.7.3.6, page 1057) defines some requirements for formatted output functions in ostreams:

Each formatted output function begins execution by constructing an object of class sentry . If this object returns true when converted to a value of type bool , the function endeavors to generate the requested output. If the generation fails, then the formatted output function does setstate(ios_base::failbit) , which might throw an exception. If an exception is thrown during output, then ios::badbit is turned on 328 in *this ’s error state. If (exceptions()&badbit) != 0 then the exception is rethrown. Whether or not an exception is thrown, the sentry object is destroyed before leaving the formatted output function. If no exception is thrown, the result of the formatted output function is *this .

(Emphasis mine)

For the construction of the sentry object (as with the construction of any object), the program will require more memory. Whether it is static or dynamic memory is left unspecified. Also, as blacks' answer sums up nicely, the standard defines that an exception may be thrown when failbit is enabled.

Not a real meerkat
  • 5,604
  • 1
  • 24
  • 55