va_arg()

L

LIT

Hello,

I have a question about va_arg() usage :
Is there any way how I can check or ensure at compile time that
va_arg() is not called when there are
no next arguments anymore.
I understood that calling va_arg() when there are no next arguments
causes an undefined behaviour.

Thanks
 
R

Richard Tobin

LIT said:
Is there any way how I can check or ensure at compile time that
va_arg() is not called when there are
no next arguments anymore.

No, your program has to work that out for itself. Typically one
of the arguments indicates how many other arguments there are (as
in printf()) or a special value (such as 0) is used as an extra
terminating argument.

-- Richard
 
M

mark_bluemel

LIT said:
I have a question about va_arg() usage :
Is there any way how I can check or ensure at compile time that
va_arg() is not called when there are
no next arguments anymore.

No - it is essential that you have a means (usually based on your first
argument) to determine how many times to invoke va_arg().

See (for example)
http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/s15.html
and
http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/q15.8.html
I understood that calling va_arg() when there are no next arguments
causes an undefined behaviour.

Yep. (My manual page talks of "random errors" :)
 
L

LIT

(e-mail address removed) schreef:
No - it is essential that you have a means (usually based on your first
argument) to determine how many times to invoke va_arg().

See (for example)
http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/s15.html
and
http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/q15.8.html


Yep. (My manual page talks of "random errors" :)

I understand
Nevertheless, if I have a function like this :

foo(unsigned char Nvars, ...)
{
unsigned int i;
unsigned int test[10];
va_list tag ;
va_start(tag,Nvars);
for (i=0 ; i<Nvars ; i++)
{
test = va_arg(tag, unsigned int);
}
va_end(tag)
}

and I do this (eg. accidentally, during coding) :

foo(4, x, y) ; (x and y are unsigned int params),

then there is no clear means to detect this at compile time
 
D

David Resnick

LIT said:
(e-mail address removed) schreef:
No - it is essential that you have a means (usually based on your first
argument) to determine how many times to invoke va_arg().

See (for example)
http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/s15.html
and
http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/q15.8.html


Yep. (My manual page talks of "random errors" :)

I understand
Nevertheless, if I have a function like this :

foo(unsigned char Nvars, ...)
{
unsigned int i;
unsigned int test[10];
va_list tag ;
va_start(tag,Nvars);
for (i=0 ; i<Nvars ; i++)
{
test = va_arg(tag, unsigned int);
}
va_end(tag)
}

and I do this (eg. accidentally, during coding) :

foo(4, x, y) ; (x and y are unsigned int params),

then there is no clear means to detect this at compile time


When we had this issue, I wrote a perl script that scanned our source
files to ensure that all calls to a predefined set of varargs functions
had a NULL as the last argument, that being what we used as a sentinel.
It wasn't perfect, but it was helpful. You might be able to come up
with a similar tool that meets your needs.

-David
 
M

mark_bluemel

LIT said:
if I have a function like this :

foo(unsigned char Nvars, ...)
and I do this (eg. accidentally, during coding) :

foo(4, x, y) ; (x and y are unsigned int params),

then there is no clear means to detect this at compile time

That's true - because the compiler doesn't know (and the language
provides no means of defining) that the first parameter to foo() has
any specific meaning.

The foo() implementation and the foo() invocation are independently
parsed and compiled (they could be in separate source files, you just
happened to combine declaring and defining foo() in your example).
 
L

Laurent Deniau

LIT said:
(e-mail address removed) schreef:

LIT wrote:



No - it is essential that you have a means (usually based on your first
argument) to determine how many times to invoke va_arg().

See (for example)
http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/s15.html
and
http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/q15.8.html



Yep. (My manual page talks of "random errors" :)


I understand
Nevertheless, if I have a function like this :

foo(unsigned char Nvars, ...)
{
unsigned int i;
unsigned int test[10];
va_list tag ;
va_start(tag,Nvars);
for (i=0 ; i<Nvars ; i++)
{
test = va_arg(tag, unsigned int);
}
va_end(tag)
}

and I do this (eg. accidentally, during coding) :

foo(4, x, y) ; (x and y are unsigned int params),

then there is no clear means to detect this at compile time


You can compute the number of arguments automatically by writing a macro
with the same name and use the macro PP_NARG I posted on comp.std.c some
time ago
(http://groups.google.com.au/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb?hl=en):

extern void foo(int n, ...);

#define foo(...) foo(PP_NARG(__VA_ARGS__)-1, __VA_ARGS__)

foo(x,y) -> foo(2,x,y)

The case:

foo() -> foo(0)

is a bit more complex but possible.

a+, ld.
 
D

David Tiktin

Nevertheless, if I have a function like this :

foo(unsigned char Nvars, ...)
{
unsigned int i;
unsigned int test[10];
va_list tag ;
va_start(tag,Nvars);
for (i=0 ; i<Nvars ; i++)
{
test = va_arg(tag, unsigned int);
}
va_end(tag)
}

and I do this (eg. accidentally, during coding) :

foo(4, x, y) ; (x and y are unsigned int params),

then there is no clear means to detect this at compile time


I'd love to hear about a clean, portable solution to this, but I don't
know of one. In the case you give, where the first argument is the
number of varargs, you could use a macro to help a little:

#define FOO_ARGS_1(a) 1, (a)
#define FOO_ARGS_2(a,b) 2, (a), (b)

etc. I'm not claiming it's pretty, but you should at least get a
compiler warning if you invoke one of the macros with the wrong number
of arguments.

Dave
 
E

Eric Sosman

LIT wrote On 10/26/06 11:23,:
[...]
Nevertheless, if I have a function like this :

foo(unsigned char Nvars, ...)
{
unsigned int i;
unsigned int test[10];
va_list tag ;
va_start(tag,Nvars);
for (i=0 ; i<Nvars ; i++)
{
test = va_arg(tag, unsigned int);
}
va_end(tag)
}

and I do this (eg. accidentally, during coding) :

foo(4, x, y) ; (x and y are unsigned int params),

then there is no clear means to detect this at compile time


Right, because the compiler doesn't know how you are going
to figure out the number and types of the optional arguments.
The scheme you use could be arbitrarily complicated: Maybe the
number of optional arguments is the square root of the first
argument, or maybe foo() stops looking for optional arguments
once their running sum exceeds the value of the first one, or
maybe the first argument is actually a bunch of bit-fields that
encode descriptions of the optional arguments, or ...

Some compilers have been "taught" the rules for variable-
argument library functions (Standard and otherwise), and can
do at least a partial validity check at compile time. At least
one compiler provides a way for you, the programmer, to state
that your print_in_colors() function is "printf-like" and can
be checked similarly. But I haven't heard of a compiler that
allows you to add new checking rules of your own.

By the way: Elsethread, someone mentioned using NULL as
a sentinel to mark the end of the list of optional arguments.
Thought question: Why is this a bad idea, and how could it
be made better? (Hint: What is the type of the expression
the NULL macro expands to?)
 
C

Christopher Benson-Manica

Eric Sosman said:
By the way: Elsethread, someone mentioned using NULL as
a sentinel to mark the end of the list of optional arguments.
Thought question: Why is this a bad idea, and how could it
be made better? (Hint: What is the type of the expression
the NULL macro expands to?)

I assume you're talking about the general case - obviously NULL as a
sentinel could not work for a function such as printf() where the
type of each argument varies. I fail to see how using a sentinel
value to terminate a list of arguments *of the same type* is a bad
idea and I assume that's not what you were getting at.
 
D

David Resnick

Christopher said:
I assume you're talking about the general case - obviously NULL as a
sentinel could not work for a function such as printf() where the
type of each argument varies. I fail to see how using a sentinel
value to terminate a list of arguments *of the same type* is a bad
idea and I assume that's not what you were getting at.

I think his point is that you need to cast NULL to void* when passing
to a varargs function because it may be just a naked 0 and int may be
smaller than void*. I didn't make up the code in question, it
preceeded me. But fixing it (with a cast or some other approach)
doesn't seem worthwhile to me as it is unlikely to break any time soon.

-David
 
K

Keith Thompson

Christopher Benson-Manica said:
I assume you're talking about the general case - obviously NULL as a
sentinel could not work for a function such as printf() where the
type of each argument varies. I fail to see how using a sentinel
value to terminate a list of arguments *of the same type* is a bad
idea and I assume that's not what you were getting at.

The point is that we don't *know* the type of the expression the NULL
macro expands to; it could be either int or void* (there are other
possibilities).

"(void*)NULL" is a better sentinel value.
 
K

Keith Thompson

David Resnick said:
I think his point is that you need to cast NULL to void* when passing
to a varargs function because it may be just a naked 0 and int may be
smaller than void*. I didn't make up the code in question, it
preceeded me. But fixing it (with a cast or some other approach)
doesn't seem worthwhile to me as it is unlikely to break any time soon.

What do you mean by "any time soon"? It could break the moment you
recompile it on a 64-bit platform.

You don't actually have to wait for the code to fail before you fix
it.
 
E

Eric Sosman

Keith Thompson wrote On 10/26/06 14:55,:
The point is that we don't *know* the type of the expression the NULL
macro expands to; it could be either int or void* (there are other
possibilities).

"(void*)NULL" is a better sentinel value.

Right. Or (char*)NULL, or (struct thing*)NULL, or
whatever pointer type the function expects to retrieve
with va_arg(). The point I wanted to make is that a
"bare" NULL makes an unreliable sentinel, because its
type is implementation-defined and quite likely wrong
for the purpose.
 
D

David Resnick

Keith said:
What do you mean by "any time soon"? It could break the moment you
recompile it on a 64-bit platform.

The code in question is in a rather stable state (past QA/load
tested/released to customers). Changing a few hundred lines with no
actual functional defect would clearly not be approved -- and rightly
so I think. I don't consider fixing this critical, just something to
keep in mind, particularly for when we migrate to 64 bits, where no
doubt other code will be destabilized too. With our current headers,
wouldn't matter even then for our C files, as NULL is defined as
(void*) 0, but those system headers could change too, and as our
sources include both C and C++ it will break at least in the C++ files
as NULL there is 0...
You don't actually have to wait for the code to fail before you fix
it.

Depending on the state of a project and the perceived risk of the
failure, sometimes you do.

-David
 
K

Keith Thompson

David Resnick said:
Keith Thompson wrote: [...]
You don't actually have to wait for the code to fail before you fix
it.

Depending on the state of a project and the perceived risk of the
failure, sometimes you do.

Fair enough.
 
C

Christopher Benson-Manica

Keith Thompson said:
The point is that we don't *know* the type of the expression the NULL
macro expands to; it could be either int or void* (there are other
possibilities).
"(void*)NULL" is a better sentinel value.

Agreed, I appreciate the clarification.
 

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

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top