Passing Variable Args

M

msr

I am trying to implement a logger. I need to pass variable number of
arguments from one func to another. Only way I could come up with was
by using va_list in the first function and pass it second function.

void Log(const char* format, ...)
{
va_list args;
va_start(args, format);
DoLog1(format, &args);
va_end;

va_list args;
va_start(args, format);
DoLog2(format, &args);
va_end;
}

void DoLog1(const char* format, va_list* args)
{
printf(format, args);
}

void DoLog2(const char* format, va_list* args)
{
// Do Something Fancy
}

This works fine but is very error prone. i.e Something like the
following will compile without any warnings and gives wrong results.

Log("Print %d", 3.00); //This prints 16 instead of 3.
Log("Print %d", (int)3.00); //This correctly prints 3.

Is there anyway I can fix this so that compiler can still emit
warnings in the above case? Thank you,

--MSR
 
B

Ben Pfaff

msr said:
This works fine but is very error prone. i.e Something like the
following will compile without any warnings and gives wrong results.

Log("Print %d", 3.00); //This prints 16 instead of 3.
Log("Print %d", (int)3.00); //This correctly prints 3.

Some compilers (notably GCC) have language extensions to mark
functions as taking printf-like variable arguments. Check in
your implementation's reference manual.
 
P

Peter Nilsson

I am trying to implement a logger. I need to pass variable
number of arguments from one func to another. Only way I
could come up with was by using va_list in the first
function and pass it second function.

void Log(const char* format, ...)
{
     va_list args;
     va_start(args, format);
     DoLog1(format, &args);

You generally don't need to pass a _pointer_ to a va_list.
     va_end;

ITYM va_end(args);
     va_list args;

This violates a constraint, even in C99 (6.7p3.)
     va_start(args, format);
     DoLog2(format, &args);
     va_end;

Same issues as above.
}

void DoLog1(const char* format, va_list* args)
{
    printf(format, args);

You need vprintf.
This works fine

Does it? ;-)
but is very error prone.
i.e Something like the following will compile without
any warnings and gives wrong results.

Log("Print %d", 3.00); //This prints 16 instead of 3.

This will compile without warnings from gcc -W -Wall -ansi
-pedantic...

printf("%d\n", -1u);
Log("Print %d", (int)3.00); //This correctly prints 3.

 Is there anyway I can fix this so that compiler can
still emit warnings in the above case?

Depends on the compiler. It's not a required diagnostic,
even for printf. But the thing about log files is, they
tend to get checked more often than output! The error
you've shown should get picked up immediately in most
cases (unless you're outputting 0.0, which will happen
to 'work' on a lot of platforms.)
 
M

msr

You generally don't need to pass a _pointer_ to a va_list.


ITYM va_end(args);

Yes. Thanks for pointing that.
This violates a constraint, even in C99 (6.7p3.)

Sorry. Whats wrong with this?
Same issues as above.



You need vprintf.
Yes.


Does it? ;-)



This will compile without warnings from gcc -W -Wall -ansi
-pedantic...

  printf("%d\n", -1u);



Depends on the compiler. It's not a required diagnostic,
even for printf. But the thing about log files is, they
tend to get checked more often than output! The error
you've shown should get picked up immediately in most
cases (unless you're outputting 0.0, which will happen
to 'work' on a lot of platforms.)

Probably. But I don't want to confuse and waste some poor developer's
time trying debug if he runs into this issue. I will check gcc manual
as per Ben's advice. So from what I understand there is no real way to
fix this :(.
 
N

Nobody

Sorry. Whats wrong with this?

6.7p3 says:

[#3] If an identifier has no linkage, there shall be no more
than one declaration of the identifier (in a declarator or
type specifier) with the same scope and in the same name
space, except for tags as specified in 6.7.2.3.

IOW, you have to pick a different name for the second occurrence. Or just
use the same variable twice:

va_list args;
va_start(args, format);
DoLog1(format, args);
DoLog2(format, args);
va_end(args);
 
N

Nobody

Some compilers (notably GCC) have language extensions to mark
functions as taking printf-like variable arguments. Check in
your implementation's reference manual.

Example:

#if !defined __GNUC__
#undef __attribute__
#define __attribute__(x)
#endif

void Log(const char* format, ...) __attribute__((format(printf,1,2)))

This will generate a diagnostic if the arguments don't match the format
specifier.

More details at:

http://gcc.gnu.org/onlinedocs/gcc-4.3.3/gcc/Function-Attributes.html#index-Wformat-2139
 
N

Nick Keighley

Sorry. Whats wrong with this?

6.7p3 says:

       [#3] If an identifier has no linkage, there shall be no more
       than one declaration of the identifier (in a  declarator  or
       type  specifier)  with  the  same scope and in the same name
       space, except for tags as specified in 6.7.2.3.

IOW, you have to pick a different name for the second occurrence. Or just
use the same variable twice:

     va_list args;
     va_start(args, format);
     DoLog1(format, args);
     DoLog2(format, args);
     va_end(args);

I think

va_list args;

va_start(args, format);
DoLog1(format, args);
va_end(args);

va_start(args, format);
DoLog2(format, args);
va_end(args);

though the magic that might be going on behind va_start
and va_end makes me a little nervous
 
M

msr

Sorry. Whats wrong with this?

6.7p3 says:

       [#3] If an identifier has no linkage, there shall be no more
       than one declaration of the identifier (in a  declarator  or
       type  specifier)  with  the  same scope and in the same name
       space, except for tags as specified in 6.7.2.3.

IOW, you have to pick a different name for the second occurrence. Or just
use the same variable twice:

     va_list args;
     va_start(args, format);
     DoLog1(format, args);
     DoLog2(format, args);
     va_end(args);

Ahh. Stupid Me. Copy paste effect and didn't notice it. Thanks
 
M

msr

Example:

        #if !defined __GNUC__
        #undef __attribute__
        #define __attribute__(x)
        #endif

        void Log(const char* format, ...) __attribute__((format(printf,1,2)))

This will generate a diagnostic if the arguments don't match the format
specifier.

More details at:

http://gcc.gnu.org/onlinedocs/gcc-4.3.3/gcc/Function-Attributes.html#...

Thanks for the pointer. Looks like this is what I am looking for.
 
J

jameskuyper

msr said:
I am trying to implement a logger. I need to pass variable number of
arguments from one func to another. Only way I could come up with was
by using va_list in the first function and pass it second function.

void Log(const char* format, ...)
{
va_list args;
va_start(args, format);
DoLog1(format, &args);
va_end;

va_list args;
va_start(args, format);
DoLog2(format, &args);
va_end;
}

void DoLog1(const char* format, va_list* args)
{
printf(format, args);
}

void DoLog2(const char* format, va_list* args)
{
// Do Something Fancy
}

This works fine but is very error prone. i.e Something like the
following will compile without any warnings and gives wrong results.

Log("Print %d", 3.00); //This prints 16 instead of 3.
Log("Print %d", (int)3.00); //This correctly prints 3.

Is there anyway I can fix this so that compiler can still emit
warnings in the above case? Thank you,

The Unix 'lint' function established the following convention, which
is recognized not only by lint, but also by many compilers: Inserting
the comment /*PRINTFLIKEn*/, where 'n' represents a number, causes the
first n-1 arguments of the next function's declaration to be checked
normally, but the nth argument is treated as a printf() format string
which is used to validate the remaining arguments.
 
N

Nobody

     va_list args;
This violates a constraint, even in C99 (6.7p3.)
Sorry. Whats wrong with this?

6.7p3 says:

       [#3] If an identifier has no linkage, there shall be no more
       than one declaration of the identifier (in a  declarator  or
       type  specifier)  with  the  same scope and in the same name
       space, except for tags as specified in 6.7.2.3.

IOW, you have to pick a different name for the second occurrence. Or just
use the same variable twice:

     va_list args;
     va_start(args, format);
     DoLog1(format, args);
     DoLog2(format, args);
     va_end(args);

I think

va_list args;

va_start(args, format);
DoLog1(format, args);
va_end(args);

va_start(args, format);
DoLog2(format, args);
va_end(args);

Er, right; 7.15p3:

The object ap may be passed as an argument to another
function; if that function invokes the va_arg macro with
parameter ap, the value of ap in the calling function is
indeterminate and shall be passed to the va_end macro prior to
any further reference to ap.
though the magic that might be going on behind va_start
and va_end makes me a little nervous

I don't see anything that precludes the above. Moreover, 7.15.1.4p3 says:

[#3] The va_start macro initializes ap for subsequent use by
va_arg and va_end. va_start shall not be invoked again for the
same ap without an intervening invocation of va_end for the
same ap.

which implies that multiple calls to va_start are permissible.
 
P

Peter Nilsson

Ahh. Stupid Me. Copy paste effect and didn't notice it.

Well if this thread should show anything it's that whilst
variadic function calls and implementation are indeed
'very error prone', responsibility for the error lies with
the programmer.

The more tools you use to find your errors, the more you
condition yourself to rely (unneccasrily) on those tools.
When I spend any time manually trawling through code to
find a bug, I tend to be much more wary the next time I
code up the same construct.

Coding practices and QA procedures tend to be much more
effective at reducing bug rates than software diagnostic
tools.
 

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,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top