Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling (possibly-changing) error codes of a library using exceptions

Let's say you are using a library that returns error codes. You'd like to write a wrapper for the library, and you'd like to handle errors with exceptions in the code.

If the library is still in development by someone else, and if the error codes may change (there can be new ones, there can be deprecated ones, or some error codes may change meaning slightly), what would your solution be to handle this?

This is the situation that I'm in right now. In my case, the library is written in C++, and we're using C#. The coder of the library says the error codes may change, and I have to find a way to work with it.

Our initial solution is to:

  1. Create an XML file that has the error codes in different categories (terminal ones, input errors, and so on).
  2. Wrapper fetches these error codes on start.
  3. Throws the appropriate exception by checking the category of the error code.

So let's say a method returns error code 100, then the wrapper checks the category of the error code. If it is a terminal error it throws a terminal error exception, if it is a user input error it throws a user input error exception.

This should work, but I feel like this is not the optimal solution. I'd like to know how good written enterprise software handle change of error codes.

What would you suggest doing?

Edit: I have already questioned the fact that error codes will be changing and the coder of the library says the code is in development. It's an algorithm, so even the way the algorithm works changes as it's original research (he's writing his PhD on that). So he says there may be different errors, or some may be irrelevant in the future.

like image 368
hattenn Avatar asked Jan 31 '13 10:01

hattenn


2 Answers

The data-driven approach you're taking, using the XML file, seems like a good one, given the circumstances. However I'd question why the error codes are changing at all - this suggests that no proper design has been carried out for the library being developed. It ought to have a well-defined structure for its error codes, rather than requiring you to keep changing your interpretation of them.

You may want to try having an overall "library exception" exception class, and subclassing it for each different type of exception you want to throw based on the "type" of the library error. At least that way, you can catch all library errors, even if one of the specific types of exception slips through the net. ie. you'd catch something like LibraryException after trying to catch TerminalErrorException.

like image 96
Jez Avatar answered Oct 19 '22 23:10

Jez


I guess you will solve this problem easier if you change your vision of the situation a little bit:

  1. You are dealing with the framework, let's call that an external framework.
  2. On the other hand, you are writing a wrapper for the framework - internal framework.
  3. Your code (client application) uses internal framework, assuming that it provides the functionality used for the problem domain. As I understand, and as I believe, client application should not have any idea about the external framework.

Now, the question comes down to the following one: is the internal framework's functionality clearly outlined and finalized? or is that changing too?

If it's changing (possibly because of the external framework), then the internal framework is under the development. This means, client application needs to wait until internal framework is ready to announce a first version ready (possibly after the external framework is complete).

Now error handling:

Errors in the application serve like contracts. Caller of the function expects particular exceptional situations, and particular kinds of errors only. Each possible error is predefined and documented by each function, similar to its input parameters and return values.

What it means for you:

  1. Define the final design of the internal framework (the sooner the better).
  2. Decide what kinds of errors each function of the internal framework can throw.
  3. Use internal framework from your client application and expect only expected and documented exceptions. Don't try/catch anything that is not expected from the internal framework. Basically, follow the contract.
  4. If error code changes, that does not change the concept of the function in the internal framework. It still needs to throw the same kind of error it threw before (according to the contract). The only part that needs to be changed is, how to translate the new code to one of the expected (contracted) errors. You can solve it any way that works better.

Why is the last assumption fine? because we said the internal application's design is final and is not going to change. Error contracts are part of the final design too.

Example:

//external.
int Say(char* message);

//internal.
///<summary>
/// can throw (CONTRACT): WrongMessageException, SessionTimeOutException
void Say(string message) {
    int errorCode = External.Say(message);
    //translate error code to either WrongMessageException or to SessionTimeOutException.
}

Cannot translate? something is wrong either with current contracted errors or the external framework: maybe you should terminate the process? something went wrong, unexpected!!!

//client.
...
try {
    Internal.Say("Hello");
}
catch (WrongMessageException wme) {
    //deal with wrong message situation.
}
catch (SessionTimeOutException stoe) {
    //deal with session timeout situation.
}

Let me know if anything raises the question.

Translating error codes to Exceptions:

This obviously is some kind of categorizing for each error code. Category can be each destination exception, and exceptions can be categorized by functions. This is exactly what the error contract means: categorize Exceptions by functions; and categorize error codes by exceptions.

Below is a pseudo configuration for this. Take this as an initial idea of how to categorize:

category Say [can throw]: { WrongMessageException, SessionTimeOutException }
category WrongMessageException [by error code]: { 100, 101 }
category SessionTimeOutException [by error code]: { 102, 103, 104 }

Of course you don't need to write a parser for such kind of impressions (this was human readable pseudo configuration). You can store similar sentences using XML or any kind of source, which will help you configure error translation rules and function contracts.

Reference

Book: Jeffrey Richter - CLR via C#, 3rd edition. Chapter 20 - Exceptions and State Management. Sub-Chapter - Guidelines and Best Practices. Sub-Sub-Chapter - Hiding an Implementation Detail to Maintain a "Contract".

This chapter will describe exceptions as contracts and will explain how to categorize contracts thrown by the function. This can confirm the correctness and the credibility of the explanations provided here.

like image 22
Tengiz Avatar answered Oct 20 '22 00:10

Tengiz