Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exceptions and error codes: mixing them the right way

I am developing a C++ dongle communication library. The library would provide an unified interface to communicate with a range of remote code execution dongles like SenseLock, KEYLOK, Guardant Code.

The dongles are based on a smart card technology and have an internal file system and RAM.

A typical operation routine involves (1) enumeration of dongles connected to the USB ports, (2) connection to a dongle selected, (3) execution of the named module passing input and collecting output data.

Well, it is trivial that all of these stages can end up with an error. There can be many cases, but the most general are:

  • A dongle is not found (sure a fatal case).
  • A dongle connection failure (a fatal case).
  • The execution module specified is not found within the dongle (?).
  • The operation requested failed due to timeout (?).
  • The operation requested needs authorization (a recoverable case I suppose).
  • A memory error occurred while executing a module in a dongle (sure a fatal case).
  • A file system error occurred in a dongle (sure a fatal case).

? - I don't know yet if the case is considered fatal or not.

I am still deciding whether to throw exceptions, to return an error codes, or to implement a methods for both cases.

The questions are:

  1. Do exceptions replace the error codes completely or maybe I need to use them only for "fatal cases"?
  2. Is mixing two paradigms (exceptions and error codes) considered a good idea?
  3. Is it good idea to provide user with two conceptions?
  4. Are there any good examples of the exceptions and error codes mixing conception?
  5. How would you implement this?

Update 1.

It would be interesting to see more opinions from different perspectives, so I decided to add a 100 reputation bounty to the question.

like image 923
ezpresso Avatar asked Apr 27 '11 14:04

ezpresso


People also ask

How do you handle exceptions and errors?

We can recover from exceptions by either using try-catch block or throwing exceptions back to the caller. All errors in java are unchecked type. Exceptions include both checked as well as unchecked type. Errors are mostly caused by the environment in which program is running.

What are exceptions and how are they different to errors?

Errors are usually raised by the environment in which the application is running. For example, an error will occur due to a lack of system resources. Exceptions are caused by the code of the application itself. It is not possible to recover from an error.

What is one way in which exceptions are superior to error codes?

The biggest benefit exception handling has over error codes is that it changes the flow of execution, which is important for two reasons. When an exception occurs, the application is no longer following it's 'normal' execution path.

Why is Java exception a better alternative of returning error codes?

An application that uses exceptions is more robust than an application that uses return codes. An application that uses exceptions can also give the cleanest code, since return codes don't have to be checked after every call.


2 Answers

If we are talking about C++ application/module internal error-handling policy then my personal opinion is:

Q: Do exceptions replace the error codes completely or maybe I need to use them only for "fatal cases"?

A: They do. Exceptions are always better for C++ function than returning error codes. The reason is explained below.

Q: Is mixing two paradigms (exceptions and error codes) considered a good idea?

A: No. Mixing is ugly and makes error handling inconsistent. When I'm forced to use error-returning API (like Win32 API, POSIX, etc) I use exception throwing wrappers.

Q: Is it good idea to provide user with two conceptions?

A: No. Users are confused which variant to choose and usually make the worst decision of mixing both. Some users prefer exceptions others prefer error-returning and if all of them work on the same project they make project's error-handling practice a total mess.

Q: Are there any good examples of the exceptions and error codes mixing conception?

A: No. Show me if you find one. IMO when writing C++ code isolating error returning functions with exception throwing wrappers is the best practice if you have to use error-returning functions (and you usually do have to use them).

Q: How would you implement this?

A: In C++ code I would use exceptions only. My way is returning only in the case of success. Error-returning practice heavily messes the code with multi-level error-checking branches or more typically and even worse - error status checking is missing and thus error status is ignored that make the code full of hidden bugs that is hard to find.

Exceptions make error propagation inevitable and error handling isolated. If you need to handle some kind of error in-place it usually means that it is not an error at all but just some legitimate event that may be reported by successful return with some specific status indicated (by return value or otherwise). If you really need to check if some error occurred locally (not from the root try/catch block) you can try/catch locally so using only exceptions doesn't really limit your capabilities in any way.

Important Note:

For every particular situation it is very important to correctly define what is error and what is not - for best usability.

E.g. say we have a function that shows input dialog and return text entered by the user and if the user may cancel the input then cancel event is success - not error (but it must be somehow indicated on return that user canceled the input) but lack of resources (like memory or GDI objects or something) or something like the absence of display device to show the dialog is indeed error.

In general:

Exceptions are more natural error-handling mechanism for C++ language. So using exceptions is a good idea if you are developing C++ application or library to be used by C++ application only (not by C application, etc). Error-returning is more portable approach - you may return error codes to applications written on any programming language and even running on different computer. Of course typically OS API routines report their status via error-codes - it is natural to make them language-independent. And for that reason you have to deal with error-codes in every-day programming. BUT IMO planning error-handling policy of C++ application to be based on error-codes is just asking for trouble - the application becomes a totally unreadable mess. IMO the best way to deal with status codes in C++ application is using C++ wrapper functions/classes/methods to call error-returning functionality and if error is returned - throw exception (with all status info embedded into exception class).

Some important notes and caveats:

In order to use exceptions as error-handling policy in a project it is important to have a strict policy of writing exception safe code. It basically means that every resource is acquired in constructor of some class and more importantly released in destructor - this makes sure you don't have resource leaks. And also you have to catch exceptions somewhere - usually in your root-level function - like main or window procedure or thread procedure, etc.

Consider this code:

SomeType* p = new SomeType;  some_list.push_back(p); /* some_list is a sequence of raw pointers so each must be delete-ed    after removing it from this list and when clearing the list */ 

It is typical potential memory leak - if push_back throws an exception then dynamically allocated and constructed SomeType object is leaked.

Exception safe equivalent is this:

C++2011 and later variant:

std::unique_ptr<SomeType> pa( new SomeType );  some_list.push_back(pa.get()); pa.release(); /* some_list is a sequence of raw pointers so each must be delete-ed    after removing it from this list and when clearing the list */ 

obsolete C++1998/C++2003 variant:

std::auto_ptr<SomeType> pa( new SomeType );  some_list.push_back(pa.get()); pa.release(); /* some_list is a sequence of raw pointers so each must be delete-ed    after removing it from this list and when clearing the list */ 

But actually storing raw pointers is bad exception-unsafe practice in general so more appropriate C++ 2011 and later variant is:

std::shared_ptr<SomeType> pa = std::make_shared<SomeType>(); //OR std::unique_ptr  some_list.push_back(pa); /* some_list is a sequence of smart pointer objects    so everything is delete-ed automatically */ 

(in C++2003 and before you could use boost::shared_ptr or your own properly designed smart pointer)

If you are using C++ standard templates, allocators, etc. you either write exception safe code (if you try/catch every single STL call the code becomes a mess) or leave the code full of potential resource leaks (that is unfortunately happen very often). Well written C++ application is just always exception safe. Period.

like image 125
Serge Dundich Avatar answered Sep 18 '22 15:09

Serge Dundich


Are there any good examples of the exceptions and error codes mixing conception?

Yes, boost.asio is the ubiquitious library used for network and serial communication in C++, and nearly every function comes in two versions: exception-throwing and error-returning.

For example, iterator resolve(const query&) throws boost::system::system_error on failure, while iterator resolve(const query&, boost::system::error_code & ec) modifies the reference argument ec.

Of course what is good design for a library, is not a good design for an application: the application would do best to use one approach consistently. You're creating a library, though, so if you're up for it, using boost.asio as a model could be a workable idea.

like image 44
Cubbi Avatar answered Sep 20 '22 15:09

Cubbi