Using exceptions: a bad style?!

Good day again,
today I have a more general question about safe programming!
I will base it on a simple example about std::string: it MAY happen, there is not enough memory to construct it from some other character container, but constructors do not return any value. So, exceptions MUST be used to be sure, unless some "paranoid" comparing is done after construction (or operator= too!).
Well, why not, but it is a conclusion for me, that I MUST check for exceptions, which may seam a "waste of time" and seams strange for those having long programmed in C. For example Linus Torvalds is apparently not going to accept C++.

And the question really is: why are there people, who say they are using C++, but not exceptions? Are they refraining from using string, vector etc? I found advices for Mozilla developers not to use exceptions, because they "are not portable"! (see https://developer.mozilla.org/En/C___Portability_Guide )

If exceptions can't be used, it turns out to me, considerable parts of C++ become useless. At lest unsafe. Your opinion?
C++ provides a flexible environment. It needs to do so to support a variety of environments.

C++ does not enforce a particular paradigm or style or standard. It allows you use a particular paradigm or style or standard.

It's quite fine for a project to say, "I want this policy" or "I want that policy", and that's fine. But C++ has a much larger scope than any one project and should not be subject to global constraints.

For example, an environment that supports exceptions is more costly to implement than one that does not. Just because you use C++ does not mean that you have to be saddled with this burden if you don't want it.

RTTI is an extra cost too. And again you have a choice.

C++ apps shouldn't be constrained to running on a traditional OS' with multi-gigahetz processors and gigabytes of RAM.

Mozilla is an old(ish) portable app that predates stable implementations of newer C++ features. It's been ported to many OS' against different versions of the compiler. We haven't always had the luxury of stable implementations of STL, exceptions, RTTI ...
Last edited on
You can use the nothrow new in order to avoid exceptions to be thrown by constructors (and most assignment operators, for that matter, as they are implemented by means of copy constructor and a (nothrow) swap). Exception safety is a major and nontrivial issue.
If exceptions can't be used, it turns out to me, considerable parts of C++ become useless. At lest unsafe.

That is certainly true. On that note, Mozilla's software isn't exactly the most stable I have encountered. However, if you know what exceptions could occur and under which circumstances, but you don't catch them explicitly, you can still write better (i.e., safer) code that if you have big "catch(...)"-blocks everywhere but don't really know what exceptions could occur and why (to quote Herb: "Exception safety doesn't mean 'try'-ing").
Considering your example: most memory allocation strategies of the operating system prevent the exception "bad_alloc" to be thrown when out of memory. Did you know? I guess not; so my code, even if I don't check for this particular exception (because it most likely isn't thrown anyways), might be safer, since I might expect the error to occur at another time with another symptom, and check for that. You might catch the exception and expect everything to be fine afterwards, which might be premature.
For example, an environment that supports exceptions is more costly to implement than one that does not. Just because you use C++ does not mean that you have to be saddled with this burden if you don't want it.
Hey, but what choice do I have about std::string? Not to use it? Make some extra comparing?? They appear the two of other choices besides exceptions.
You might catch the exception and expect everything to be fine afterwards, which might be premature.
Thank you. Well, this all makes me opposed to use std::string and other "nice" elements of STL. To hell, if I can't know easily if they work, why should I use them?! (Oh, I remind me Linus about this!) It appears then very clear about why are big codes using their own base types. For example OpenFOAM: it has its own string, label, various lists and tables and so on.
IMHO most people don't use exceptions either because they don't know about them or they don't know how to use them. It is not easy to write exception-safe code.

I would not say that projects use their own custom string class to avoid std::string's use of exceptions. I've worked on many projects (professionally) and most of the time the decision is made for other reasons such as the need for a thread-safe string implementation, etc.

The exceptions thrown by STL can probably be broadly classified into two categories: 1) out of memory, and 2) your own programming error.

Your own custom code has to deal with out of memory conditions anyway, so you aren't buying anything there. And the second one isn't even STL's fault, so again, you would have to deal with the error yourself anyway. For example, accessing an element of a vector<> using at() that does not exist. Your own custom vector<> class would need to give some kind of error back to the caller, so why not use an exception?

In many cases programmers simply ignore out of memory conditions because there is little that can be done. For example, printf( "Out of memory\n" ); may very well try to allocate memory which will itself fail. Do I catch std::bad_alloc ever in my code? No, for the above reason and also for the reason that helios mentioned above: in Linux, for example, with overcommit_memory turned on, the act of allocating memory will fail ONLY if your process runs out of virtual address space. Linux does not actually reserve physical memory for the allocation until you write to the pages you just allocated.

There are two error handling styles that work for lagre projects:

Style 1 - "C-style". Try not to throw exceptions. *All* functions return a status code. Check for error after *every* function call, ever. In this style, every function call is normally 5-6 lines of code.

1
2
3
4
5
6
7
8
9
bool frobby;
int status;

status = CheckForFrobs(thing, &frobby);
if ( status != SUCCESS )
{
    LOG_AN_ERROR(__FUNCTION__, __LINE__);
    goto cleanup;
}

Note the goto. You must either do RAII-style coding, or have a cleanup block at the bottom of every funtion, and never return from the middle (or you'll get memory leaks and handle leaks).


Style 2 - exception safe. All functions must be exception safe. All classes must automatically clean up after themselves (usually RAII style). This is hard to learn how to do correctly. However, you can seriously shorten your code by 80%.

1
2
3
bool frobby;

frobby = CheckForFrobs(thing);

Note how the logic flow is obvious here, but takes some work to spot in the C style.

I'm convinced that there's a signiicant attachment to the C style by people who are measured by lines of code written, not results (which is far too common).


Last edited on
most memory allocation strategies of the operating system prevent the exception "bad_alloc" to be thrown when out of memory. Did you know? I guess not;
Just in case, I would like to ask if C malloc can be prevented from returning 0 after no memory has actually been allocated? (I am not meaning hardware errors)

Also:
if I have a code like this:

1
2
3
void fx(){
int i = 0;
}
Is it possible that there is no memory for i?
Last edited on
That's stack memory, not heap memory. If you run out of stack memory I think your program will crash with a stack overflow error (not sure whether or not an exception is thrown).
IMHO most people don't use exceptions either because they don't know about them or they don't know how to use them. It is not easy to write exception-safe code.

+1

@Karlis: No. If you declare the variable locally there should always be enough memory for it. The stack should grow to accomidate it.
Style 2 - exception safe. All functions must be exception safe. All classes must automatically clean up after themselves (usually RAII style). This is hard to learn how to do correctly. However, you can seriously shorten your code by 80%.
To be honest, I have no idea what "exception safe" means. Maybe "for every throw there is a catch"? Also how about computation performance drop, if exceptions are used in some code with big executing redundancy?
Having execution safe code (entering a try block) is extremely slow.
A piece of code is said to be exception-safe, if run-time failures within the code will not produce ill effects, such as memory leaks, garbled stored data, or invalid output. Exception-safe code must satisfy invariants placed on the code even if exceptions occur. There are several levels of exception safety:

Failure transparency, also known as the no throw guarantee: Operations are guaranteed to succeed and satisfy all requirements even in presence of exceptional situations. If an exception occurs, it will not throw the exception further up. (Best level of exception safety.)

Commit or rollback semantics, also known as strong exception safety or no-change guarantee: Operations can fail, but failed operations are guaranteed to have no side effects so all data retain original values.

Basic exception safety: Partial execution of failed operations can cause side effects, but invariants on the state are preserved. Any stored data will contain valid values even if data has different values now from before the exception.

Minimal exception safety also known as no-leak guarantee: Partial execution of failed operations may store invalid data but will not cause a crash, and no resources get leaked.

No exception safety: No guarantees are made. (Worst level of exception safety)

from Exception handling From Wikipedia, the free encyclopedia http://en.wikipedia.org/wiki/Exception_handling
Last edited on
Karlis:
To be honest, I have no idea what "exception safe" means. Maybe "for every throw there is a catch"?

An exception safe class is one that is guaranteed to clean up any resources that it allocates. Exception-safe code uses only such classes, and never contains a "naked" new, malloc, file open, or anything else that requires a matching close/free.

For example, if the class has a file handle, the destructor closes the handle; if the class allocates memory, the destructor frees memory. Because this can be hard to get right 100% of the time, you build such classes from simple primitives, such as auto_ptr, shared_ptr, and a self-closing file handle. Ideally, your destructor should have nothing to clean up, as all member variables should be self-cleaning.

Putting classes like that on the stack (local variables) is exception safe. There is never any "clean-up" code at the bottom of a function - ever. Since all classes clean themselves up, you never have to care whether a function exits normally, exits with error, or something it calls throws.


Zaita:
Having execution safe code (entering a try block) is extremely slow.

I believe the total cost in the MS compiler of entering a try block is pushing one more argument on the stack. I'd be interested to see evidence of "extremely slow". Unwinding the stack on a throw can be costly, of course, but no mor so than returning normally "up the stack" would have been.

In any case, if you have lots of try/catch blocks, you're doing it wrong. The only places you should catch exceptions are at a "formal API boundary" (such as a dll boundary) or in those rare cases that you can actually do something about the problem that caused the exception.
Topic archived. No new replies allowed.