What's The Best Practice Defining Error Codes in C

  • Thread starter ׿ǿ Zhuo, Qiang
  • Start date
×

׿ǿ Zhuo, Qiang

Hi,

I would like to have someone comments on what's the best practice
defining error codes in C.
Here's what I think:
solution A:
using enum
pros: type safe. better for debug (some debugger will show the name
not only the value)
cons: enum can not be forward declared which makes all error codes
couples together with the error code type ( enum )

Solution B:
using #define
pros: decouple, error codes could be defined in different .h file
cons: macro is bad. no type safe

Solution C:

typedef struct Error
{
int value;
} Error;

static Error const ERROR_OUT_OF_SPACE = { 123 };

pros: type safe. decouple, the error code type is no longer bound with
all the error definition
cons: I don't know any one doing it this way so I'm not sure if it has
some drawbacks or is it bad for (runtime/space) performance.
If using pure C, user could not compare the error value directly but
have to compare the inner "value" member which is not convenience.

Thanks for your help :)

Qiang
 
E

Eric Sosman

׿ǿ Zhuo said:
Hi,

I would like to have someone comments on what's the best practice
defining error codes in C.
Here's what I think:
solution A:
using enum
pros: type safe. better for debug (some debugger will show the name
not only the value)
cons: enum can not be forward declared which makes all error codes
couples together with the error code type ( enum )

By "can not be forward declared," I guess you mean that
you cannot just invent a new error code at some random place
in the program; is that right? If so, I'd say this should be
considered a "pro" and not a "con."
Solution B:
using #define
pros: decouple, error codes could be defined in different .h file
cons: macro is bad. no type safe

"Macro is bad" is bad.
"`Macro is bad' is bad" is bad.
"`«Macro is bad» is bad' is bad" is bad.
...
Solution C:

typedef struct Error
{
int value;
} Error;

static Error const ERROR_OUT_OF_SPACE = { 123 };

Careful! If you include the <errno.h> header -- or if any
code that refers to ERROR_OUT_OF_SPACE includes <errno.h> --
Then all identifiers beginning with an E and another upper-case
letter or beginning with E and a digit are reserved.
pros: type safe. decouple, the error code type is no longer bound with
all the error definition

If by "decouple" you mean that it is now possible to invent
new error codes at any random place in the program, I'd say this
should be a "con" rather than a "pro." For one thing, there's
no convenient way to be sure error codes aren't duplicated: what
if somebody defines OUT_OF_CHEESE_ERROR with the value 123?
cons: I don't know any one doing it this way so I'm not sure if it has
some drawbacks or is it bad for (runtime/space) performance.
If using pure C, user could not compare the error value directly but
have to compare the inner "value" member which is not convenience.

Right: Opacity is lost. Also, so is contant-ness, in the
sense that the error objects are not compile-time constants.
You can't use them as `case' labels, for example.

Solutions (A) and (B) are the most common in code I've seen
over the years. I've never seen (C), not in such a bare-bones
form at any rate.
 
Z

Zhuo, Qiang ׿ǿ

     By "can not be forward declared," I guess you mean that
you cannot just invent a new error code at some random place
in the program; is that right?  If so, I'd say this should be
considered a "pro" and not a "con."

Yes and No. As a module We'd like to split the error code to two
category, those for the client and those inside. We don't want to
define all the error code in one place which introduce unnecessary
couple. But for one category, it's better that they are defined in one
place.

     "Macro is bad" is bad.
     "`Macro is bad' is bad" is bad.
     "`«Macro is bad» is bad' is bad" is bad.
     ...
So I guess you dislike macro either :)
     Careful!  If you include the <errno.h> header -- or if any
code that refers to ERROR_OUT_OF_SPACE includes <errno.h> --
Then all identifiers beginning with an E and another upper-case
letter or beginning with E and a digit are reserved.
Thanks for the reminder. Actually all identifiers are prefixed with
module name.(how long will namespace be introduced into C)
     If by "decouple" you mean that it is now possible to invent
new error codes at any random place in the program, I'd say this
should be a "con" rather than a "pro."  For one thing, there's
no convenient way to be sure error codes aren't duplicated: what
if somebody defines OUT_OF_CHEESE_ERROR with the value 123?
Yes, By decouple I mean the possibility to define new error code
somewhere else. I know it requires discipline to do so. Like I said
we'd like to separate private error code from public interface.
     Right: Opacity is lost.  Also, so is contant-ness, in the
sense that the error objects are not compile-time constants.
You can't use them as `case' labels, for example.

Good point. I guess I have to drop the C) choice

     Solutions (A) and (B) are the most common in code I've seen
over the years.  I've never seen (C), not in such a bare-bones
form at any rate.

I will stick with solution A) for now.

However, another silly question:

How about:

typedef struct ErrorTag* Error;
static Error const = 123;

I'm living in C++ world, so I want to try my best to make the API type
safe. Is there any way except enum could give a type safe error code
solution ?

Thanks for your time :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top