Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > What's The Best Practice Defining Error Codes in C

Reply
Thread Tools

What's The Best Practice Defining Error Codes in C

 
 
rammy
Guest
Posts: n/a
 
      06-29-2012
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
 
Reply With Quote
 
 
 
 
rammy
Guest
Posts: n/a
 
      06-29-2012
Hello? is this forum be down??

 
Reply With Quote
 
 
 
 
Keith Thompson
Guest
Posts: n/a
 
      06-29-2012
rammy <(E-Mail Removed)> writes:
> Hello? is this forum be down??


You asked your question 36 minutes ago. Be patient.

--
Keith Thompson (The_Other_Keith) http://www.velocityreviews.com/forums/(E-Mail Removed) <http://www.ghoti.net/~kst>
Will write code for food.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
Stefan Ram
Guest
Posts: n/a
 
      06-29-2012
rammy <(E-Mail Removed)> writes:
>using #define
>pros: decouple, error codes could be defined in different .h file
>cons: macro is bad. no type safe


Most libraries I am aware of, use #define IIRC. So, I'd use that, too.

But there are more decisions to be made:

Do you want to have codes be unique per function, per library, or
globally (as far as code was written under your control)?

Do you want codes to be structured (so that certain bits have certain
meanings) or unstructured?

Heck, sometimes, I just use naked literals: 1, 2, 3, and so ... .

 
Reply With Quote
 
Eric Sosman
Guest
Posts: n/a
 
      06-29-2012
On 6/29/2012 4:29 PM, rammy wrote:
> 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 )


"Type safe" is too optimistic. An enum is some kind of integer
type, and integer types are freely interconvertible (as long as the
values are in range). A named enum value is just an `int' constant,
and can be stored in any integer variable (of sufficient range).
Try it yourself:

enum Fruit { APPLE, BANANA, CHERRY } fruit;
enum Motor { OTTO, DIESEL, STEAM } motor;
fruit = OTTO;
motor = BANANA;
fruit = PEAR * STEAM;
motor = sqrt(42.0);
printf("%g\n", cos(motor));
...

> 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 -- well, that statement is bad.

> Solution C:
>
> typedef struct Error
> {
> int value;
>
> } Error;
>
> static Error const ERROR_OUT_OF_SPACE = { 123 };


Aside: Are you aware that ERROR_OUT_OF_SPACE is a reserved
name whenever <errno.h> is included?

> 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.


This is type safe, but in a limited way. If the struct
declaration is visible (as it would have to be to allow access
to the embedded member), there's nothing to prevent someone
creating and returning an Error value you've never heard of:

Error do_something(void) {
...
Error x;
x.value = rho * sin(theta);
return x;
}

.... could inject an "unauthorized" code into your system. This
could be either a pro or a con, depending on your point of view.

You could regain some type safety by declaring the struct as
an incomplete type in the public header, declaring some instances
of it, and using pointers to those instances as the error codes:

// In the header:
typedef struct Error *Error;
extern struct Error OUT_OF_SPACE;
extern struct Error OUT_OF_SIGHT;
extern struct Error OUT_OF_MIND;
...

// Usage:
Error do_something(void) {
...
return &OUT_OF_MIND;
}
Since `struct Error' is incomplete, no one can create a new
instance to point at and thereby give rise to unknown codes.
Of course, even a moderately determined antagonist can still
cause trouble with a cast:

Error be_troublesome(void) {
return (Error)42;
}

To the wider question, I don't think there's a "One size fits
all" solution. Sometimes you need only a success/failure report,
and this sort of machinery is overkill. Sometimes success and
failure come in several kinds (non-overlapping, not too many), and
schemes of the sort you describe may be appropriate. Sometimes you
need to report several things about a failure or success (not just
"login failed" but "login failed because authentication server did
not respond because no connection to authentication server because
a network cable is unplugged"), and a single-code-per-failure-mode
approach would suffer combinatorial explosion.

Instead of seeking a single "best" error-reporting scheme (which
I doubt exists), I suggest you contemplate the library or other
facility that you're building, and ask yourself what kinds and amounts
of status information the callers can make good use of. You're likely
to come up with different scenarios for different facilities, so pick
a facility-specific approach -- and implement it well.

--
Eric Sosman
(E-Mail Removed)d


 
Reply With Quote
 
Malcolm McLean
Guest
Posts: n/a
 
      06-30-2012
בתאריך יום שישי, 29 ביו*י 2012 21:29:33 UTC+1, מאת rammy:
>
> I would like to have someone comments on what's the best practice
> defining error codes in C.
>

The way I do it is this. I divide the soruce files for the program into four groups: this program only, this platform only. this program only, any platform, this platform only, any program. Any program, any platform.
So a random number generator would most likely be "any program,any platform". Code to make the spaceship fire when the player presses the spacebar would probably be this platform only, this program only.
For the last two groups, the "any program" files, you need to pass up errors to the caller. Let's say that someone tries to generate a random number with a range too big for our generator to handle. We might decide that in this case, we'll always return -1. The process is ad hoc, and there shouldn'tbe any dependency. We need to be able to cut and paste our random number generator from the space invaders game, put it into a roulette game or a protein structure predictor, and have it still work.
For the first two groups, the files will never be used outside of the specific project. So it might be appropriate to have centralised errors with a file "errorcodes.h" somewhere containing a list of human-meaningful enums.
 
Reply With Quote
 
rammy
Guest
Posts: n/a
 
      06-30-2012
On Fri, 29 Jun 2012 17:25:49 -0400, Eric Sosman wrote:

> On 6/29/2012 4:29 PM, rammy wrote:
>> 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 )

>
> "Type safe" is too optimistic. An enum is some kind of integer
> type, and integer types are freely interconvertible (as long as the
> values are in range). A named enum value is just an `int' constant, and
> can be stored in any integer variable (of sufficient range). Try it
> yourself:
>
> enum Fruit { APPLE, BANANA, CHERRY } fruit; enum Motor { OTTO,

DIESEL,
> STEAM } motor; fruit = OTTO;
> motor = BANANA;
> fruit = PEAR * STEAM;
> motor = sqrt(42.0);
> printf("%g\n", cos(motor));
> ...


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.

>> 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 -- well, that statement is bad.


So I guess you dislike macro either

>> Solution C:
>>
>> typedef struct Error
>> {
>> int value;
>>
>> } Error;
>>
>> static Error const ERROR_OUT_OF_SPACE = { 123 };

>
> Aside: Are you aware that ERROR_OUT_OF_SPACE is a reserved
> name whenever <errno.h> is included?



Thanks for the reminder. Actually all identifiers are prefixed with
module name.(how long will namespace be introduced into C)

>> 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.

>
> This is type safe, but in a limited way. If the struct
> declaration is visible (as it would have to be to allow access to the
> embedded member), there's nothing to prevent someone creating and
> returning an Error value you've never heard of:
>
> Error do_something(void) {
> ...
> Error x;
> x.value = rho * sin(theta);
> return x;
> }
>
> ... could inject an "unauthorized" code into your system. This could be
> either a pro or a con, depending on your point of view.
>
> You could regain some type safety by declaring the struct as
> an incomplete type in the public header, declaring some instances of it,
> and using pointers to those instances as the error codes:
>
> // In the header:
> typedef struct Error *Error;
> extern struct Error OUT_OF_SPACE;
> extern struct Error OUT_OF_SIGHT;
> extern struct Error OUT_OF_MIND;
> ...
>
> // Usage:
> Error do_something(void) {
> ...
> return &OUT_OF_MIND;
> }
> Since `struct Error' is incomplete, no one can create a new instance to
> point at and thereby give rise to unknown codes. Of course, even a
> moderately determined antagonist can still cause trouble with a cast:
>
> Error be_troublesome(void) {
> return (Error)42;
> }


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.

> To the wider question, I don't think there's a "One size fits
> all" solution. Sometimes you need only a success/failure report, and
> this sort of machinery is overkill. Sometimes success and failure come
> in several kinds (non-overlapping, not too many), and schemes of the
> sort you describe may be appropriate. Sometimes you need to report
> several things about a failure or success (not just "login failed" but
> "login failed because authentication server did not respond because no
> connection to authentication server because a network cable is
> unplugged"), and a single-code-per-failure-mode approach would suffer
> combinatorial explosion.
>
> Instead of seeking a single "best" error-reporting scheme (which
> I doubt exists), I suggest you contemplate the library or other facility
> that you're building, and ask yourself what kinds and amounts of status
> information the callers can make good use of. You're likely to come up
> with different scenarios for different facilities, so pick a
> facility-specific approach -- and implement it well.


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
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      06-30-2012
rammy <(E-Mail Removed)> writes:
<snip>
> 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 ?


What does "living in C++ world" mean? Do you mean you are used to C++'s
more sophisticated error reporting facilities, or it slightly stronger
type checking?

C is not a type-safe language (neither is C++ for that matter) so unless
you say what you are prepared to compromise on there is no solution at
all.

--
Ben.
 
Reply With Quote
 
Ian Collins
Guest
Posts: n/a
 
      06-30-2012
On 07/ 1/12 08:57 AM, rammy wrote:

<snip>

> 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.


A couple of projects I have worked on solved this by defining the error
codes in something other than C and generating the C header files from
there. So each set got its own header, but the overall definitions were
elsewhere. This had the bonus advantage of providing the "extensible
enum" support C lacks.

> 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 ?


You will have to accept that from a C++ programmer's perspective, enums
in C are somewhat broken due to automatic conversions. While C++ rules
prevent silly mistakes like

typedef enum { Good, Bad } Result;

Result oops() { return -1; }

C unfortunately does not. So from a type safety perspective, enums are
no better than #defines.

--
Ian Collins
 
Reply With Quote
 
Eric Sosman
Guest
Posts: n/a
 
      06-30-2012
On 6/30/2012 4:57 PM, rammy wrote:
> On Fri, 29 Jun 2012 17:25:49 -0400, Eric Sosman wrote:
>
>> On 6/29/2012 4:29 PM, rammy wrote:
>>> 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 )

>>
>> "Type safe" is too optimistic. An enum is some kind of integer
>> type, and integer types are freely interconvertible (as long as the
>> values are in range). A named enum value is just an `int' constant, and
>> can be stored in any integer variable (of sufficient range). Try it
>> yourself:
>>
>> enum Fruit { APPLE, BANANA, CHERRY } fruit; enum Motor { OTTO,

> DIESEL,
>> STEAM } motor; fruit = OTTO;
>> motor = BANANA;
>> fruit = PEAR * STEAM;
>> motor = sqrt(42.0);
>> printf("%g\n", cos(motor));
>> ...

>
> 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.


Perhaps my typo caused you to miss the point What I mean
is that an enum is not "type safe" as you seem to think. C will
not prevent you from setting an `enum X' variable to an `enum Y'
value, nor to `42', nor to the result of some arbitrary expression.

enum X { A, B, C } foobar(void);
switch (foobar()) {
case A: ... break;
case B: ... break;
case C: ... break;
default: ... // Yes, this case *can* occur.
}

>>> 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 -- well, that statement is bad.

>
> So I guess you dislike macro either


Macros are a part of the C language. They are sometimes useful,
sometimes dangerous -- like most of the rest of C. The blanket
statement "macro is bad" is nonsense.

>>> Solution C:
>>>
>>> typedef struct Error
>>> {
>>> int value;
>>>
>>> } Error;
>>>
>>> static Error const ERROR_OUT_OF_SPACE = { 123 };

>>
>> Aside: Are you aware that ERROR_OUT_OF_SPACE is a reserved
>> name whenever <errno.h> is included?

>
> Thanks for the reminder. Actually all identifiers are prefixed with
> module name.(how long will namespace be introduced into C)


Probably never, although no deity has granted me the gift of
prophecy and I could be mistaken. If namespaces ever *are* added
to C, their use will be "mostly optional" in the name of backwards
compatibility: The investment in existing non-namespace C code is
enormous, and cannot simply be abandoned.

> [...]
> 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.


Extensible error codes (extensible codes of any kind) require
discipline from both the producer and the consumer. For example,
if a caller uses foobar() and checks for SUCCESS or OUT_OF_SPACE,
a future foobar() version that starts reporting QUOTA_EXCEEDED as
well may break the caller. Silently. So: "Be careful out there."

> However, another silly question:
>
> How about:
>
> typedef struct ErrorTag* Error;
> static Error const = 123;


The compiler is required to issue a diagnostic, for at least
two reasons: First, the second line looks like a declaration but
declares no identifier. Second, the constant `123' cannot be
converted automatically to any kind of pointer. I cannot tell
what you intended to write, so I can't comment on "How about."

> I'm living in C++ world, [...]


Then you're in the wrong newsgroup. comp.lang.c++ is just
down the hall to your right, past the broom closet.

--
Eric Sosman
(E-Mail Removed)d


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re What's The Best Practice Defining Error Codes in C Sergi Pasoev C Programming 0 06-29-2012 09:15 PM
What's The Best Practice Defining Error Codes in C ׿ǿ Zhuo, Qiang C Programming 2 11-17-2008 04:16 PM
Virtual Key Codes, Scan Codes and ASCII Codes in C gj_williams2000@yahoo.co.uk C Programming 2 08-20-2005 11:04 AM
Remember when your piano teacher taught you, "Practice, practice,practice ...?" Wayne Wastier Windows 64bit 3 06-10-2005 08:29 PM
defining or not defining destructors johny smith C++ 8 07-02-2004 08:51 AM



Advertisments