views:

214

answers:

7

When writing a function, my implementation very frequently looks like this:

  • Call a subfunction
  • If this subfunction fails to execute (because of an exceptional situation): log this failure and abort the current function
  • Otherwise continue calling other subfunctions, which in turn can fail

A crucial part is the logging. Every function that fails should add a short description to the log. This way, at the level where the exception is handled, the user can be shown a detailed error message.

For example, consider an application where a new user account can be created, and there is a problem with the database connection. The following inverse stack trace results:

  • SQLDriverConnect() -> "SQLDriverConnect error: Data source not found and no default driver specified"
  • OpenDatabaseConnection() -> "Failed to open database connection"
  • CreateUser() -> "Failed to create the new user"
  • ValidateAndSaveNewUserAccount() -> "Failed to save the user profile"
  • Catch the exception and show the logged messages to the user

Using the exceptions feature, I would implement this as follows:

void CreateUser()
{
    try {
        OpenDatabaseConnection();
    }
    catch(std::exception& e) {
        e.AddLog("Failed to create the new user");
        throw;
    }
    //...
}

Using a simple return value, I'd write the following:

bool CreateUser(Log& log)
{
    if (!OpenDatabaseConnection(log))
    {
        log.Add("Failed to create the new user");
        return false;
    }
    //...
    return true;
}

I find both implementations equally good. Therefore, I don't see much advantage in using exceptions. I am well aware that exception handling is generally considered a useful feature, but I don't really understand why. A long time ago, I used exception handling extensively, but I didn't see the big advantage, so now I never use them anymore. Hence my questions:

  • Using exceptions, how can this be implemented in C++ more concisely?
  • If not, what is then the advantage of throwing exceptions over returning an "is successful" boolean?

Note: I use the term logging as "collecting an explanation of what went wrong, so it can be presented to the user later on". I'd rather not store that explanation in a global collection of log messages (in memory, in a file or in a database), as it directly describes the specific exception.

Update: Thanks for you responses so far. I understand that exceptions are only useful if the user doesn't need detailed feedback on what went wrong. (Please correct me if I misinterpreted this.)

+2  A: 

I think a big case for using exceptions here is that you've now made logging part of your method signatures. In general, I don't think that should be the case, because it's a cross-cutting concern. Imagine trying to do an analogous thing with user permissions, for example. Are you going to write this everywhere?

bool CreateUser(Log& log, UserSecurityContext& u) {
    if (!HasPermissionsFor(u, SecurityRoles::AddUser)) {
        log.Add("Insufficient permissions");
        return false;
    }
    //...
    return true;
}

There are other reasons to want to use exceptions (see Elemental's answer), but anytime the non-use of a language feature impacts the design of your software, it's worth thinking about whether that was the right way to do it.

John Feminella
Thanks for the reply. Only: are you suggesting that you could eliminate the UserSecurityContext parameter by using exceptions?
Dimitri C.
@Dimitri C. » Right; it shouldn't be part of the method signature (imo).
John Feminella
But how would you do it? How can exceptions eliminate the need for the UserSecurityContext parameter?
Dimitri C.
+5  A: 

Your strategy seems to avoid the most useful aspect of exceptions, you can throw an exception class which already has the text of the log information in it - that is generate the text for the log at time the exception is thrown not at the time the exception is caught. Then you don't have to catch at every level going up the stack, but only at the top level.

Thus only one try block, and one log.add - much less code in general. Something like this seems to remove all your replication.

void OpenDatabaseConnection()
{
   if (Error) throw MyException("Failed opening database");
}

void CreateUser()
{
    try {
        OpenDatabaseConnection();
        //...... do everything here
    }
    catch(MyException& E) { //only one copy of this code
        E.AddLog(E.getMessage());
        throw;
    }
}
Elemental
The problem is that this way, the user looses essential information. The log at the level where the problem is detected is too specific for the user, whereas the log at the level where the exception is eventually caught is to general.
Dimitri C.
You CAN still trap exceptions lower in the stack if it is useful to do so - in other words you only need the catch block where you have a good reason for them. You can also throw a new exception from inside the catch block summarising detail that you don't want to pass up the stack.
Elemental
You are right: in cases where you don't need to add detailed logging, exceptions are somewhat more concise.
Dimitri C.
Instead of MyException::getMessage I prefer to use a method named what which is compatible with std::exception public interface, virtual const char* what() const throw();See for instance:http://www2.roguewave.com/support/docs/leif/sourcepro/html/stdlibref/exception.html
Francesco
Yeah, I think deriving your own exception type from std::exception is a good idea - just didn't want to confuse the issue here.
Elemental
+2  A: 

If you always want to handle your exceptional conditions immediately after the call, then there is no real advantage.

The advantage comes when you want to handle the condition several layers up the call chain. To do that with your success flag, you'd have to bubble the flag up several layers of subroutine calls. Every layer would have to be written with the knowldege that it has to keep track of the special flag from way down in the bowels of the code. This is just a major primo PITA.

For instance, for realtime work we typically build our applications around an iteration loop. Any error during the loop generally just aborts that iteration of the loop (excepting "fatal" errors, which abort the entire app). The easiest way to handle this is to just throw exceptions from wherever they occur, and handle them all in their own catch blocks at the very outermost of the application.

T.E.D.
Thank for the response, but are you saying that feedback to the user is not that important?
Dimitri C.
No, I'm saying nothing of the sort. For example, our standard exception class has a log message as a member. That can get thrown with the exception. The the catch handler has the choice of logging the exception with either its own message, the thrown message, or both.
T.E.D.
+1  A: 

Exception handling removes error handling from the normal control flow. This way, the code structured more clean. Exception handling also unwinds the stack automatically. This way, you need not to include error handling code in each method called on the way to the error. If you need one of those features, go with exceptions. If you don't, use error-codes or any other method because exceptions have costs (computing time) even if they are not thrown.


Additional answers to your comment. Imagine a code, that calls several functions that may fail.

procedure exceptionTest(...)
{
  try
  {
    call1();
    call2();
    call3();
    call4();
  } 
  catch (...)
  {
    //errorhandling outside the normal control flow
  }
}

without exception:

procedure normalTest(...)
{
   if (!call1())
   {
     //errorHandling1
   } 
   else if (!call2())
   {
     //errorHandling2
   }
   else if ....
   ...
}

As you can easily see, the normal control flow is disrupted with error handling. Compared to this code, the code using exceptions is easier to read.

If you need to add error handling in each method you call, exceptions may not provide benefits. But if you have nested calls that each may generate errors, it may be easier to catch the exception at top level. That's what I meant. It is not the case in your example, still it's good to know where to benefit from exceptions.

Tobias Langner
"removes error handling from the normal control flow": With the example I give, I try to demonstrate that this doesn't work.
Dimitri C.
"you need not to include error handling code in each method": yes you do: to add user-readable logging.
Dimitri C.
+1  A: 

Exceptions are using in only extreme situations. Execution of exception is too slow. For log not great errors try use return value.

Example:

int someMethod{
if(erorr_file_not_found){
logger.add("File not found");
return 1;}

if(error_permission){
logger.add("You have not permissons to write this file");
return 2;}

return 0;
}

In this case you can print error and process this error on higher level.

Or (more complex):

int someMethod{
int retval=0;
if(someshit){
retval=1;
goto _return;}
//...
_return:
switch(retval){
case 1:logger.add("Situation 1");break;
case 2:logger.add("Situation 2");break;
//...
}
/*
close files, sockets, etc.
*/
return retval;
}

This way is more hard but most fast.

Staseg
A: 

Depending on your circumstances, you may be able to log from a constructor of your exception (maybe asynchronously) That way your code would look like:

void CreateUser()
{
      OpenDatabaseConnection();
}

Of course, you would need to throw your custom exception from OpenDatabaseConnection().

I worked on two projects when this strategy was used with success.

Nemanja Trifunovic
A: 

I would propose to separate error handling from logging and from the user interaction.

  1. Every method can write to log files for itself. With a small log message framework, methods can output debug, informational and error message. Depending on the context your applications runs in defined by a config file, e.g., only critical error messages are actually written.

  2. Especially in networking applications, connection failures can always occur and are not exceptional. Use exceptions for unexpected errors that should not occur or that occur only rarely. It can also make sense to use exceptions internally if you need e.g. the stack unrolling feature:

    void CreateUser() {
        try {
           CDatabaseConnection db = ConnectToDatabase(); 
           InsertIntoDB(db, "INSERT INTO ... "); 
           SetPermission(...);
        } catch(...) {}
    }
    

    If InsertIntoDB throws an exception because the network connection is lost again, object CDatabaseConnection will be destroyed and SetPermission is never run. Using this can lead to better code.

  3. The third thing you want to do is give the user feedback in an interactive application. That's a whole different thing. Let your internal methods return enumerations of possible error codes eerrorCONNECTIONLOST, eerrorUSERNAMEINVALID, etc Don't return the error strings from core methods. The user interface layer should bother which strings to display (possibly internationalizing them). Internally, the error codes will be much more useful. You could e.g. retry five times if your login method returned eerrorCONNECTIONLOST.

Sebastian
I use the term logging as "collecting an explanation of what went wrong, so it can be presented to the user later on". I'd rather not store that explanation in a global collection of log messages (in memory, in a file or in a database), as it directly describes the specific exception. Instead, I want to keep it close to the exception.
Dimitri C.
"Don't return the error strings from core methods": Hm; this seems like an awful lot of work to me.
Dimitri C.