va_arg problem

M

Mark

Hi all

My c is a bit rusty and my brain is dead!

Can anyone tell me why the following trys to print when I call
pferror(E1); ...

#include <stdio.h>
#include <stdarg.h>

#define UE 0
#define E1 1
#define E2 2
#define E3 3
#define E4 4

char *error_msg[] = {
"Undefined error",
"Error msg 1",
"Error msg 2",
"Error msg 3",
"Error msg 4",
};

/* Print formatted error */
void pferror (int, ...);

int
main ()
{
pferror(E1);
pferror(E2, "Msg (E2)");
pferror(E3, "Msg (E3) (%d)", 111);
pferror(E4, "Msg (E4) (%d)(%s)", 222, "Three");
return 0;
}
void
pferror (int error, ...)
{
va_list args;
char *fmt;

fprintf(stderr, "Error: %s ", error_msg[error]);

va_start(args, error);
fmt = va_arg(args, char *);
if(fmt)
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
}
 
C

Chris Dollin

Mark said:
My c is a bit rusty and my brain is dead!

Can anyone tell me why the following trys to print when I call
pferror(E1); ...

Because you have undefined behaviour.
int
main ()
{
pferror(E1);
(fx:snip)

void
pferror (int error, ...)
{
va_list args;
char *fmt;

fprintf(stderr, "Error: %s ", error_msg[error]);

va_start(args, error);
fmt = va_arg(args, char *);
if(fmt)

There's no second argument, so the result of `va_arg` is undefined.

`va_arg` isn't specified to return null if there's no corresponding
argument -- how could it be, since you can ask for `int` or `float`
args with no null value to return?

It's up to you to /know/ if there's supposed to be va_arguments.
 
M

Mark

Can anyone tell me why the following trys to print when I call
pferror(E1); ...
Note that should say ... trys to call vfprintf ... (definately a brain
dead day!)
 
R

Richard Bos

Mark said:
Can anyone tell me why the following trys to print when I call
pferror(E1); ...

#include <stdio.h>
#include <stdarg.h>
/* Print formatted error */
void pferror (int, ...);

int
main ()
{
pferror(E1);
}
void
pferror (int error, ...)
{
va_list args;
char *fmt;

fprintf(stderr, "Error: %s ", error_msg[error]);

va_start(args, error);
fmt = va_arg(args, char *);
if(fmt)

Because asking va_arg() for a variable argument which isn't there
results in undefined behaviour, not in a null value. You can solve this
in two ways:
- always pass an extra argument which tells you whether there are
variable arguments, and if so, how many;
- in the specific case of no variable arguments only, pass an extra
explicit null pointer (i.e., do pferror(E1, (char *)0); ).
The first option is more work, but more consistent; the second option
introduces a special case, but is more work only for that special case.

Richard
 
M

Mark

Because asking va_arg() for a variable argument which isn't there
results in undefined behaviour, not in a null value. You can solve this
in two ways:
- always pass an extra argument which tells you whether there are
variable arguments, and if so, how many;
- in the specific case of no variable arguments only, pass an extra
explicit null pointer (i.e., do pferror(E1, (char *)0); ).
The first option is more work, but more consistent; the second option
introduces a special case, but is more work only for that special case.

Richard

Thanks Guys, I think I need to hit myself over the head with an
Harbison & Steele!

I suppose I could also call it as pferror(E1, ""), however I would
really like to call it as I attempted above, ie with a mandatory error
number plus an optional string and/or format specifiers. I'm
wondering if I could so something fancy with macros?

The other alternative is to implement some overloading but I don't
think we don't want to go down that route!

Thanks again for the feedback.

Mark.
 
C

Chris Dollin

Mark said:
Thanks Guys, I think I need to hit myself over the head with an
Harbison & Steele!

I suppose I could also call it as pferror(E1, ""), however I would
really like to call it as I attempted above, ie with a mandatory error
number plus an optional string and/or format specifiers. I'm
wondering if I could so something fancy with macros?

Just ensure that you can tell from the error number whether or not
there are additional parameters.

You could, for example, reserve the low bit of the error number
for a "has string format" flag:

pferror( BROKEN );
vs
pferror( BROKEN | FORMAT, "I broke my %s!\n", bodyPart );

with `FORMAT` being #defined or enumed to 1 and `BROKEN` being even.
 
M

Mark

You could, for example, reserve the low bit of the error number
for a "has string format" flag:

pferror( BROKEN );
vs
pferror( BROKEN | FORMAT, "I broke my %s!\n", bodyPart );
Now that's a good idea, if it was used internally at least; but if I
wanted to wrap pferror (and others) into a library I would still
prefer a single 'kind of overloaded' interface, ie pferror(E1),
pferror(En, "str"), pferror(En, "%d, %s", int_a, str_b), ..., hmmm I
don't think I'll be sleeping much tonight.
 
R

Richard Bos

Mark said:
Now that's a good idea, if it was used internally at least; but if I
wanted to wrap pferror (and others) into a library I would still
prefer a single 'kind of overloaded' interface, ie pferror(E1),
pferror(En, "str"), pferror(En, "%d, %s", int_a, str_b), ..., hmmm I
don't think I'll be sleeping much tonight.

You could insist that all errors with numbers equal to or below certain
limit (say, E_NO_FORMAT) have no format. This would require either not
linking in your library but compiling it with the users's macro
definitions, or making E_NO_FORMAT (but not necessarily any of the
others) an object rather than a macro, or setting a hard value for
E_NO_FORMAT in your library itself, and expecting its user never to use
more than, say, 256 errors without a message. All of these have their
advantages and disadvantages; I'd probably choose the second, declaring
extern const int E_NO_FORMAT in the library, and requiring the user to
define const int E_NO_FORMAT=42.

(BTW: E1, E2, and so on, are reserved for the implementation. You could
legally use E_1 &c.)

Richard
 
M

Mark

You could insist that all errors with numbers equal to or below certain
limit (say, E_NO_FORMAT) have no format. This would require either not
linking in your library but compiling it with the users's macro
definitions, or making E_NO_FORMAT (but not necessarily any of the
others) an object rather than a macro, or setting a hard value for
E_NO_FORMAT in your library itself, and expecting its user never to use
more than, say, 256 errors without a message. All of these have their
advantages and disadvantages; I'd probably choose the second, declaring
extern const int E_NO_FORMAT in the library, and requiring the user to
define const int E_NO_FORMAT=42.

Yeh, I'm thinking about a variation of your second option.
(BTW: E1, E2, and so on, are reserved for the implementation. You could
legally use E_1 &c.)
Sure, I used En for the posting only, without thinking.

Thanks for your thoughts.

Mark.
 
M

Marjancek

I suppose I could also call it as pferror(E1, ""), however I would
really like to call it as I attempted above, ie with a mandatory error
number plus an optional string and/or format specifiers. I'm
wondering if I could so something fancy with macros?

#define PFERROR(e) pferror(e, "")
#define PFERROR(e, m) pferror(e, "")

Perhaps your problem would be better solved this way?

Mariano.
 
K

Kenneth Brody

Mark said:
Now that's a good idea, if it was used internally at least; but if I
wanted to wrap pferror (and others) into a library I would still
prefer a single 'kind of overloaded' interface, ie pferror(E1),
pferror(En, "str"), pferror(En, "%d, %s", int_a, str_b), ..., hmmm I
don't think I'll be sleeping much tonight.

#define E_HasFormat 0x8000
#define E_NoFormat 0

#define E1 (1 | E_NoFormat)
#define E2 (2 | E_HasFormat)
#define E3 (3 | E_HasFormat)
....

Just make sure you document which ones require a format string.

Another option is two versions of pferror() -- one with a varadic
format, and one with no format.

Though, honestly, I think defining "you must pass a format string
and its paremeters, or NULL" is not a major hardship.

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
K

Kenneth Brody

Marjancek said:
#define PFERROR(e) pferror(e, "")
#define PFERROR(e, m) pferror(e, "")

Perhaps your problem would be better solved this way?

You can't #define the same macro twice, even if the "signature" is
different because of a differing number of parameters.

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
M

Mark

Another option is two versions of pferror() -- one with a varadic
format, and one with no format.
Yep, that's the route I've taken now, two versions plus some changes
to take different logging levels, plog for 'print log' and pflog for
'print formatted log', eg:

plog(E, E_ERROR_1);
pflog(E, E_ERROR_2, "txt");
pflog(E, E_ERROR_3, "txt (%d) [%s]", int_a, str_b);

(the E_str is generic for posting here)

The logging levels (at the moment, I'm still playing) map as follows
(similar to log4j from what I remember):
F - Fatal
E - Error
W - Warning
I - Info
D - Debug

Then each F_str, E_str etc is an index into an array of messages, one
each for fatalities, errors, etc (though for fatalities I may just
have to attempt logging then exit - it's fatal after all!)

I'll probably add an init function to set the logging location, eg
stdout, stderr, file, http?, but not sure yet.

Thanks again to everyone for suggestions.

Mark.
 
J

Joachim Schmitz

Mark said:
Another option is two versions of pferror() -- one with a varadic
format, and one with no format.
Yep, that's the route I've taken now, two versions plus some changes
to take different logging levels, plog for 'print log' and pflog for
'print formatted log', eg:

plog(E, E_ERROR_1);
pflog(E, E_ERROR_2, "txt");
pflog(E, E_ERROR_3, "txt (%d) [%s]", int_a, str_b);

(the E_str is generic for posting here)

The logging levels (at the moment, I'm still playing) map as follows
(similar to log4j from what I remember):
F - Fatal
E - Error
W - Warning
I - Info
D - Debug

Then each F_str, E_str etc is an index into an array of messages, one
each for fatalities, errors, etc (though for fatalities I may just
have to attempt logging then exit - it's fatal after all!)

I'll probably add an init function to set the logging location, eg
stdout, stderr, file, http?, but not sure yet.
If available on your system, you may want look at syslog()

Bye, Jojo
 
M

Marjancek

#define PFERROR(e) pferror(e, "")
You're right.

Why then use a default value for a fixed second parameter? We do
expect a char* anywa, right?

void pferror (int, err_msg="", ...)


Mariano
 
B

Ben Bacarisse

Marjancek said:
You're right.

You snipped the attribution line, so I had some trouble seeing who you
thought was right (made worse by the fact that you kept the erroneous
code and removed the line that said that is was wrong). Proper
quoting helps everybody (you wrote it once but, with luck, dozens of
people read it).
Why then use a default value for a fixed second parameter? We do
expect a char* anywa, right?

void pferror (int, err_msg="", ...)

C has no default arguments.
 

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,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top