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

Advertisements

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

Advertisements

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

Top