Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is GetLastError() kind of design pattern? Is it good mechanism?

Windows APIs uses GetLastError() mechanism to retrieve information about an error or failure. I am considering the same mechanism to handle errors as I am writing APIs for a proprietary module. My question is that is it better for API to return the error code directly instead? Does GetLastError() has any particular advantage? Consider the simple Win32 API example below:

HANDLE hFile = CreateFile(sFile,
    GENERIC_WRITE, FILE_SHARE_READ,
    NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);

if (hFile == INVALID_HANDLE_VALUE)
{
    DWORD lrc = GetLastError();

    if (lrc == ERROR_FILE_EXISTS)
    {
          // msg box and so on
    }
}

As I was writing my own APIs I realized GetLastError() mechanism means that CreateFile() must set the last error code at all exit points. This can be a little error prone if there are many exit points and one of them maybe missed. Dumb question but is this how it is done or there is some kind of design pattern for it?

The alternative would be to provide an extra parameter to the function which can fill in the error code directly so a separate call to GetLastError() will not be needed. Yet another approach can be as below. I will stick with the above Win32 API which is good example to analyzer this. Here I am changing the format to this (hypothetically).

result =  CreateFile(hFile, sFile,
    GENERIC_WRITE, FILE_SHARE_READ,
    NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);

if (result == SUCCESS)
{
   // hFile has correct value, process it
}
else if (result == FILE_ALREADY_EXIT )
{
   // display message accordingly
  return;
}
else if ( result == INVALID_PATH )
{
   // display message accordingly.
  return;
}

My ultimate question is what is the preferred way to return error code from an API or even just a function since they both are the same?

like image 987
zar Avatar asked Feb 01 '12 17:02

zar


People also ask

Which design pattern is used in exception handling?

Generally a try/catch/finally block is written for handling exception which could go terribly wrong very soon.

Should you use exceptions in C++?

Exceptions are preferred in modern C++ for the following reasons: An exception forces calling code to recognize an error condition and handle it. Unhandled exceptions stop program execution. An exception jumps to the point in the call stack that can handle the error.

What will happen if thrown exception is not handled in C++?

Explanation: As the func() is throwing a const char* string but we the catch block is not catching any const char* exception i.e. exception thrown is not handled therefore the program results into Aborted(core dumped).

How do you implement top level exception handling in C++?

C++ exception handling is built upon three keywords: try, catch, and throw. throw − A program throws an exception when a problem shows up. This is done using a throw keyword. catch − A program catches an exception with an exception handler at the place in a program where you want to handle the problem.


2 Answers

Overall, it's a bad design. This is not specific to Windows' GetLastError function, Unix systems have the same concept with a global errno variable. It's a because it's an output of the function which is implicit. This has a few nasty consequences:

  1. Two functions being executed at the same time (in different threads) may overwrite the global error code. So you may need to have a per-thread error code. As pointed out by various comments to this answer, this is exactly what GetLastError and errno do - and if you consider using a global error code for your API then you'll need to do the same in case your API should be usable from multiple threads.

  2. Two nested function calls may throw away error codes if the outer function overwrites an error code set by the inner.

  3. It's very easy to ignore the error code. In fact, it's harder to actually remember that it's there because not every function uses it.

  4. It's easy to forget setting it when you implement a function yourself. There may be many different code paths, and if you don't pay attention one of them may allow the control flow to escape without setting the global error code correctly.

Usually, error conditions are exceptional. They don't happen very often, but they can. A configuration file you need may not be readable - but most of the time it is. For such exceptional errors, you should consider using C++ exceptions. Any C++ book worth it's salt will give a list of reasons why exceptions in any language (not just C++) are good, but there's one important thing to consider before getting all excited:

Exceptions unroll the stack.

This means that when you have a function which yields an exception, it gets propagated to all the callers (until it's caught by someone, possible the C runtime system). This in turn has a few consequences:

  1. All caller code needs to be aware of the presence of exceptions, so all code which acquires resources must be able to release them even in the face of exceptions (in C++, the 'RAII' technique is usually used to tackle them).

  2. Event loop systems usually don't allow exceptions to escape event handlers. There's no good concept of dealing with them in this case.

  3. Programs dealing with callbacks (plain function pointers for instance, or even the 'signal & slot' system used by the Qt library) usually don't expect that a called function (a slot) can yield an exception, so they don't bother trying to catch it.

The bottom line is: use exceptions if you know what they are doing. Since you seem to be rather new to the topic, rather stick to return codes of functions for now but keep in mind that this is not a good technique in general. Don't go for a global error variable/function in either case.

like image 59
Frerich Raabe Avatar answered Sep 30 '22 21:09

Frerich Raabe


The GetLastError pattern is by far the most prone to error and the least preferred.

Returning a status code enum is a better choice by far.

Another option which you did not mention, but is quite popular, would be to throw exceptions for the failure cases. This requires very careful coding if you want to do it right (and not leak resources or leave objects in half-set-up states) but leads to very elegant-looking code, where all the core logic is in one place and the error handling is neatly separated out.

like image 37
StilesCrisis Avatar answered Sep 30 '22 21:09

StilesCrisis