error codes — some clarifications

In this post I would like to discuss two issues brought up by the readers regarding previous posts on using <system_error>:

  1. Storing 0 in error codes, and using non-0 success values.
  2. Concerns about using globals.

0 ⇔ Success?

In the other post I have mentioned that using numeric value 200 to indicate success (this is the case for HTTP status codes) is incorrect because it would make contextual conversion to bool fail to meet the intuitive expectation:

if (std::error_code ec = fun())
  handle_failure();
else
  handle_success();

This has triggered an interesting thread in isocpp.org. Let me summarize it here.

It is not uncommon to see this encoding scheme: 0 represents success, non-0 values represent different kind of failures. One such example is reflected in what we return from function main. However, there are systems that do not follow this principle. To name two examples:

  1. HTTP protocol status codes: any value between 100 an 399 actually represents success.
  2. Vulkan return codes: negative numbers represent failures, zero represents plain success, positive numbers represent success with additional status info, like “time-out occurred” (warnings).

Typically, the most relevant information we expect of a returned status code, independent of any system, is whether the operation has succeeded or failed. If it failed we have to abandon the subsequent operations that depend on the former. This construction of operation dependency is essential to any error handling system. We could create a category for HTTP error codes where comparing to value http::success would take into account all values representing success from HTTP operations, but it would not be generic: we would need a different special condition for vulkan::success, and so on. In a generic context, where we get just an std::error_code from an unknown system there would be no way of asking, “did the operation succeed or fail?”

The contextual conversion to bool could be a good candidate for such generic discrimination into success or failure, but it requires that we change the status value encoding for systems like HTTP or Vulkan. But this would compromise another design goal of <system_error>: that it can store a numeric value from any system directly — without any remapping.

A different conclusion to mine has been drawn: maybe non-zero success codes are fine, but you have to accept that there is no generic way of determining success or failure from an std::error_code. It is not that often that you need a generic check form a system library, anyway.

But one thing is clear: if you use non-zero success codes you should not rely on contextual conversion to bool.

Duplicated globals

<system_error> requires using global objects (actually, namespace-scope objects) for identifying error categories. Globals are known not to work well with DLLs on Windows. If your program is linked with a static library L, and it loads a DLL, and this DLL was also linked with the same static library L, the code of L is duplicated along with the globals it defined.

Thus the program has one instance of global G, and the DLL has the second instance, with a different address. You think that you are accessing a single global, but this is not the case: depending on whether the call is made directly from the program or from a DLL, a global from a different address is used. The same problem applies also to function-local static objects and any kind of singletons: they become doubletons.

One could ask if this behavior is standard-compliant, but since the C++ Standard does not have to say anything about shared libraries, it has also nothing to say about how they would interact with globals.

Since error categories depend on the uniqueness of the address, they will not work correctly if you use them in this configuration. I did not observe the same problems with shared libraries on Linux, although one reader in this blog claimed to the contrary.

The only solution to this problem that I am aware of, when you are using any DLL in your program, is to make sure that every library that uses globals is also made a DLL itself (rather than being statically linked):

This problem is characteristic of any library that uses globals. For instance, Boost.Log gives the same advice (see here). Maybe the only difference is that from the nature of a library like Boost.Log it is immediately clear that it will be using some globals; whereas error_codes are trivially-copyable value-semantic types, and unless you learn the implementation details you would have never expected that there are any globals involved.

This entry was posted in programming and tagged , , . Bookmark the permalink.

4 Responses to error codes — some clarifications

  1. xABBAAA says:

    Nice thing to mention is #include too.
    However, some Operating system has its own codes that are used in .Net or Linux.

    Nice stuff …

  2. Adrian says:

    Another system that uses non-zero success codes is VMS (nowadays, OpenVMS), where odd values indicate success and even ones indicate failure.

  3. xABBAAA says:

    Okay, I need to say that I red your article and it has some very good info contained.
    However, there is a way more potential into it, but it would need to have some additional opinions there.

    So!
    What do I mean, when I say that. Let’s see…
    In the first place in C there was some EXIT_SUCCESS or EXIT_FAILURE, this way will just tell us that everything is okay, or that something went wrong.
    The alternative approach was used in Pascal in particular, there were some ERROR codes that were done with some numbers, and the table was good to tell us what went wrong. For example, If you divide the number by 0 it would have its code, if you cross the border of the array it would have some code for it, etc…
    This approach is better, because it is more informative. Aldo, some problems would be created in practical explorations.

    Now, in C++, we have the options to create our objects as the protection for those nasty problems that could happened.
    Though, that is not well informative for the programmer, I think that it would be nice to have something like the shot of the system, in witch you will have the state of important registers, stacks etc…
    That would create better info for the programmer, and that will have info how that error has occurred. Then that could be sent to the site and maintenance programmers would have better info what went wrong.
    That way, it could be more unified to all operating system.

    Thus far I was not been able to find enough time to work on something nice like that…

  4. Why would anybody start a C++ project and use <system_error>, while knowing this:

    2018 — Summary of SG14 discussion on
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0824r1.html

    I am afraid it is as simple as that …

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.