Howto pass va_list to another va func

T

Tor Rustad

I have a C program, where I control the error behavior according to context:


/* some error handlers */
static void on_error_log (MYSQL *mysql, const char *msg, ...);
....
static void on_error_exit (MYSQL *mysql, const char *msg, ...);

/* the error function */
static void (*error_fatal) (MYSQL *mysql, const char *msg, ...) =
on_error_log;


i.e. on the fly I can change which handler error_fatal() uses, sometimes I
want to call exit(), other times just log error and continue etc.


So far, no problem, but then I wanted a trace function and decided to reuse
the on_error_log() function, since the trace function didn't need the MYSQL
context, a simplified interface could be used:


static void log_err(const char *msg, ...);


Now comes the problem, how to make log_err() call on_err_log(), clearly my
first try didn't work:


$ cat test_argp.c
/*
Demonstrate the problem of calling variable argument
function, from another va function.
*/

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

#define LOG_FILE_NAME "test.log"

static void on_error_log(const char *msg, ...)
{
va_list argp;
FILE *log;

assert(msg != NULL);

log = fopen(LOG_FILE_NAME, "a");
if (log == NULL)
{
perror(LOG_FILE_NAME);
exit(EXIT_FAILURE);
}

if ( msg != NULL)
{
va_start(argp, msg);
(void) vfprintf(log, msg, argp);
va_end(argp);
}

fclose(log);
}

static void log_err(const char *msg, ...)
{
va_list argp;

assert(msg != NULL);

va_start(argp, msg);
on_error_log(NULL, msg, argp);
va_end(argp);
}

int main(void)
{
const char *string = "Test string";

log_err("Log test.... string");
log_err("Log test.... string");

log_err("string = '%s'\n", string);
log_err("string = '%s'\n", string);

return EXIT_SUCCESS;
}

$ gcc -ansi -W -Wall -pedantic -g test_argp.c
$ ./a.out
a.out: test_argp.c:18: on_error_log: Assertion `msg != ((void *)0)' failed.
Aborted
$ gdb a.out
(gdb) r
Starting program: /****/***/***/******/a.out
a.out: test_argp.c:18: on_error_log: Assertion `msg != ((void *)0)' failed.

Program received signal SIGABRT, Aborted.
0xffffe410 in __kernel_vsyscall ()
(gdb) bt
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xb7e51770 in raise () from /lib/tls/i686/cmov/libc.so.6
#2 0xb7e52ef3 in abort () from /lib/tls/i686/cmov/libc.so.6
#3 0xb7e4adbb in __assert_fail () from /lib/tls/i686/cmov/libc.so.6
#4 0x080484b4 in on_error_log (msg=0x0) at test_argp.c:18
#5 0x0804856b in log_err (msg=0x80486f7 "Log test.... string") at
test_argp.c:44
#6 0x08048591 in main () at test_argp.c:52
(gdb)


so the assert() bomb out on the first logerr() call. Unless I'm missing some
trivial point here, could someone explain how to do this, and why the above
code fail.
 
F

Flash Gordon

Tor Rustad wrote, On 25/04/07 20:49:
Grrrrrrrrrrr...

on_error_log(msg, argp);

Also you have to make on_error_log take a va_list rather than being
varidac (ending in a ...). If you want to be able to call it both ways,
something like:

void von_error_log(char *msg, va_list args)
{
/* do the work */
}

void on_error_log(char *msg, ...)
{
va_list args;
va_start(args,msg);
von_error_log(msg,args);
va_end(args);
}
 
E

Eric Sosman

Tor Rustad wrote On 04/25/07 15:41,:
I have a C program, where I control the error behavior according to context:


/* some error handlers */
static void on_error_log (MYSQL *mysql, const char *msg, ...);
...
static void on_error_exit (MYSQL *mysql, const char *msg, ...);

/* the error function */
static void (*error_fatal) (MYSQL *mysql, const char *msg, ...) =
on_error_log;


i.e. on the fly I can change which handler error_fatal() uses, sometimes I
want to call exit(), other times just log error and continue etc.


So far, no problem, but then I wanted a trace function and decided to reuse
the on_error_log() function, since the trace function didn't need the MYSQL
context, a simplified interface could be used:


static void log_err(const char *msg, ...);


Now comes the problem, how to make log_err() call on_err_log(), clearly my
first try didn't work:
[...]

You were about half way home, but only half. The idea
is to put the "guts" of the operation in a function that
takes a fixed argument list, one of which is a va_list.
Then you also write one or more wrappers that take "..."
arguments, initialize a va_list, call the "guts" function,
and clean up the va_list. See Question 15.12 in the FAQ
at <http://www.c-faq.com/>.
 
T

Tor Rustad

Eric said:
Tor Rustad wrote On 04/25/07 15:41,:
[...]
Now comes the problem, how to make log_err() call on_err_log(), clearly
my first try didn't work:

You were about half way home, but only half. The idea
is to put the "guts" of the operation in a function that
takes a fixed argument list, one of which is a va_list.
Then you also write one or more wrappers that take "..."
arguments, initialize a va_list, call the "guts" function,
and clean up the va_list. See Question 15.12 in the FAQ
at <http://www.c-faq.com/>.

For some reason I must have forgot 15.12, thanks a lot!

Here, is the modified test code, which indeed worked as expected:


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

#define LOG_FILE_NAME "test.log"

static void verror_log(const char *msg, va_list argp)
{
FILE *log;

assert(msg != NULL);

log = fopen(LOG_FILE_NAME, "a");
if (log == NULL)
{
perror(LOG_FILE_NAME);
exit(EXIT_FAILURE);
}

if ( msg != NULL)
{
(void) vfprintf(log, msg, argp);
}

fclose(log);
}

static void on_error_log(const char *msg, ...)
{
va_list argp;

assert(msg != NULL);

va_start(argp, msg);
verror_log(msg, argp);
va_end(argp);
}

static void log_err(const char *msg, ...)
{
va_list argp;

assert(msg != NULL);

va_start(argp, msg);
verror_log(msg, argp);
va_end(argp);
}

int main(void)
{
const char *string = "Test string";

on_error_log("Log test.... string\n");
log_err("Log test.... string\n");

on_error_log("string = '%s'\n", string);
log_err("string = '%s'\n", string);

return EXIT_SUCCESS;
}
 

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,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top