Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > varargs's doubt

Reply
Thread Tools

varargs's doubt

 
 
grid
Guest
Posts: n/a
 
      04-05-2005
Hi,
I have a function which takes a variable number of arguments.It then
calls va_start macro to initialize the argument list.But here in my case
I have a special builtin va_start provided by the compiler which I use
for performance gains.Then the variable arguments are passed to another
varargs function which then uses a builtin va_arg macro to get the
arguments.
Iam getting an error from the compiler in the called function when a
local va_list variable is assigned to the passed va_list.Is this not
defined by the standard ?

I get the following error code :

gs.c(749): warning #563: initialization with "{...}" expected for
aggregate object
va_list ap = arglist;
^

gs.c(749): error: a value of type "struct __builtin_va_list_struct_type
*" cannot be used to initialize an entity of type "struct
__builtin_va_list_struct_type"
va_list ap = arglist;
^

compilation aborted for gs.c (code 2)


To show you how the code is in the source.Apologize to not being able to
produce a minimal compilable program exhibiting the problem.

gsscanf(char * fmt,...){
va_list ap;
.
.
__builtin_stdarg_start((ap),fmt);
ret = gsScanfV(fmt,ap);
__builtin_va_end( ap );
}

gsScanfV(char *fmt,va_list arglist)
{
va_list ap = arglist;
.
.
.
__builtin_va_arg(ap,type);
__builtin_va_arg(ap,type);
.
.

}

I could solve the above compilation error by changing the "va_list
arglist " to "va_list ap" in the gsScanfV() function parameters and
removing the assignmet line (va_list ap = arglist.
Why is that I am getting an error when I assign the passed va_list
variable to a local va_list variable ? Is this not defined by the standard ?

I would like to hear some inputs on what is defined and what undefined
with reference to the above code snippet.


TIA,
 
Reply With Quote
 
 
 
 
grid
Guest
Posts: n/a
 
      04-05-2005

> I could solve the above compilation error by changing the "va_list
> arglist " to "va_list ap" in the gsScanfV() function parameters and
> removing the assignmet line (va_list ap = arglist.


I also got another way to solve the problem.Changed the called function
as below :
gsScanfV(char *fmt,va_list arglist)
{
va_list ap ;
va_copy(arglist,ap);
.
.
.
__builtin_va_arg(ap,type);
__builtin_va_arg(ap,type);
.
.

}

Iam not sure which one looks an elegant solution.Request people to
somment on the same.

Regards,
Rohitash
 
Reply With Quote
 
 
 
 
Chris Croughton
Guest
Posts: n/a
 
      04-05-2005
On Tue, 05 Apr 2005 11:45:57 +0530, grid
<(E-Mail Removed)> wrote:

> I have a function which takes a variable number of arguments.It then
> calls va_start macro to initialize the argument list.But here in my case
> I have a special builtin va_start provided by the compiler which I use
> for performance gains.Then the variable arguments are passed to another
> varargs function which then uses a builtin va_arg macro to get the
> arguments.


For a start, I am very dubious that there are any significant
performance gains in doing that. If there were, the library
implementors should have defined the macrose in terms of the builtins in
the first place. Have you actually profiled the code and found a
significant difference there?

Try it with the standard ones, does that work? Are you including a
special header to get that builtin functionality?

> Iam getting an error from the compiler in the called function when a
> local va_list variable is assigned to the passed va_list.Is this not
> defined by the standard ?


It doesn't seem to be. C99 says that you can pass it as an argument to
a function, and to va_start/va_arg/va_end/va_copy, but the existence of
va_copy() would seem to imply that just assigning it is not a defined
behaviour (gcc/glibc seems to accept it, but that doesn't imply that
anything else would).

> I get the following error code :
>
> gs.c(749): warning #563: initialization with "{...}" expected for
> aggregate object
> va_list ap = arglist;
> ^
>
> gs.c(749): error: a value of type "struct __builtin_va_list_struct_type
> *" cannot be used to initialize an entity of type "struct
> __builtin_va_list_struct_type"
> va_list ap = arglist;
> ^


It looks as though the compiler explicitly excludes variables of type
__builtin_va_list_struct_type from being assigned. It may be
implemented as an array, for instance.

Something you could do is look at the preprocessed output and search for
__builtin_va_list_struct_type, see if that is defined anywhere.

> I could solve the above compilation error by changing the "va_list
> arglist " to "va_list ap" in the gsScanfV() function parameters and
> removing the assignmet line (va_list ap = arglist.


So why don't you do that? You said above that you are concerned about
speed, why do an extra assignment?

Chris C
 
Reply With Quote
 
grid
Guest
Posts: n/a
 
      04-05-2005
> So why don't you do that? You said above that you are concerned about
> speed, why do an extra assignment?


Chris,its not a piece of code written by me and hence wanted to confirm
if I am not treading on undefined land because of my changes.Iam not
sure why the original author chose to have an extra assignment.

Which one of the two solutions look graceful ?

TIA,
 
Reply With Quote
 
Chris Croughton
Guest
Posts: n/a
 
      04-05-2005
On Tue, 05 Apr 2005 20:03:11 +0530, grid
<(E-Mail Removed)> wrote:

>> So why don't you do that? You said above that you are concerned about
>> speed, why do an extra assignment?

>
> Chris,its not a piece of code written by me and hence wanted to confirm
> if I am not treading on undefined land because of my changes.Iam not
> sure why the original author chose to have an extra assignment.
>
> Which one of the two solutions look graceful ?


I would use the parameter directly, that's the way with least code (and
therefore the least to go wrong). Having an extra variable prompts the
question "why? was there some reason it was done that way?".

If va_copy() exists in your implementation (it's a C99 feature not in
C89) then by all means use that.

(Although I'd love to know how they managed to implement it such that a
va_list can be passed as a parameter but not assigned directly, that
smacks of special coding...)

Chris C
 
Reply With Quote
 
Chris Torek
Guest
Posts: n/a
 
      04-05-2005
>On Tue, 05 Apr 2005 11:45:57 +0530, grid
> <(E-Mail Removed)> wrote:
>> I have a function which takes a variable number of arguments.It then
>> calls va_start macro to initialize the argument list.But here in my case
>> I have a special builtin va_start provided by the compiler which I use
>> for performance gains.


It is not a "performance" item, but rather a correctness issue.
Using the __builtin_* functions will do the right thing; trying
to fake it out by hand will not, in some cases.

In any case, the __builtin_* stuff is hidden behind macros, and
you, as the programmer, should just use the advertised interface
(va_start, va_arg, va_end, and in this case, nothing else).

>>Then the variable arguments are passed to another varargs
>>function which then uses a builtin va_arg macro to get the arguments.


This is allowed, but *only* via this form:

SOMETYPE like_printf(const char *fmt, va_list ap) {
/* code that uses va_arg(ap, TYPE) */
}

(of course SOMETYPE can be any valid function-return type, the name
of the function can be any valid function name, the "fmt" argument
can be something else, and so on). The important point here is
that "ap" is a formal parameter of type "va_list". If va_list is
a typedef for an array type, C's peculiar treatment of arrays will
automatically adjust it to be "pointer to first element of that
array" instead (as is happening here).

In article <(E-Mail Removed)>
Chris Croughton <(E-Mail Removed)> wrote:
>For a start, I am very dubious that there are any significant
>performance gains in doing that. If there were, the library
>implementors should have defined the macrose in terms of the builtins in
>the first place.


You are right, and they did. (I am gazing into my crystal ball and
I see that "grid" is using a PowerPC or similar architecture.)

>It doesn't seem to be. C99 says that you can pass it as an argument to
>a function, and to va_start/va_arg/va_end/va_copy, but the existence of
>va_copy() would seem to imply that just assigning it is not a defined
>behaviour


This is correct. In particular, on the machine he has, the contents
of <stdarg.h> include something like this:

typedef struct __builtin_va_list_struct_type va_list[1];

struct __builtin_va_list_struct_type {
int __ni; /* number of saved int regs */
int __i[__VA_MAXI]; /* and their values */
int __nf; /* number of saved FPU regs */
float __f[__VA_MAXF]; /* and their values */
int *__rest; /* other values (on stack) */
};

>(gcc/glibc seems to accept it, but that doesn't imply that
>anything else would).


The machine on which va_copy is a simple copy, by contrast, has this
in its <stdarg.h>:

typedef char *va_list;

Note the (second) error message here:

>> gs.c(749): warning #563: initialization with "{...}" expected for
>> aggregate object
>> va_list ap = arglist;
>> gs.c(749): error: a value of type "struct __builtin_va_list_struct_type
>> *" cannot be used to initialize an entity of type "struct
>> __builtin_va_list_struct_type"
>> va_list ap = arglist;
>> ^


Clearly "arglist" has type "pointer to struct ...", while "ap" needs
an initializer of type "struct ...", enclosed in braces. Clearly
"ap" is actually an array type, while "arglist" is a pointer type.
How can "arglist" be a pointer type when it also uses "va_list"
as its declaration? The answer lies in The Rule about arrays and
pointers in C.

Let me show the two lines in question one more time here:

int gsScanfV(const char *fmt, va_list arglist) {
va_list ap = arglist;
...
}

If va_list is a typedef for an array type (and it is), we now
have the same thing we would get with:

void f(char arg_s[1]) {
char local_s[1] = arg_s;
...
}

Here arg_s has type "char *" (pointer to char), while local_s has
type "char [1]" (array 1 of char). The initializer is invalid
and a diagnostic must occur.

>> I could solve the above compilation error by changing the "va_list
>> arglist " to "va_list ap" in the gsScanfV() function parameters and
>> removing the assignmet line (va_list ap = arglist.


This is a good solution *provided* it is OK to destroy the va_list
object whose element-address has been passed (courtesy, again, of
The Rule). Consider for instance:

int message(const char *fmt, ...) {
va_list ap;
int ret;

va_start(ap, fmt);
ret = vfprintf(stdout, fmt, ap);
va_end(ap);
return ret;
}

This function works just like printf(). The vfprintf() function,
which receives the address of the first (and only) element of the
"struct" containing the various registers, is allowed to modify
ap[0].__ni (AKA ap->__ni) and ap->__nf and ap->__rest. Not only
is this allowed, it actually happens. Now suppose we decide that
message() should not only print to stdout, but also to a message-file.
So we try change message() to read:

int message(const char *fmt, ...) {
va_list ap;
int ret;
extern FILE *logfile;

va_start(ap, fmt);
ret = vfprintf(stdout, fmt, ap);
ret = vfprintf(logfile, fmt, ap); /* OOPS! WRONG! BUG! */
va_end(ap);
return ret;
}

As before, vfprintf() -- the first call, to stdout -- modifies
ap->__ni and ap->__nf and ap->__rest. The second call receives
these modified values -- and prints junk.

On another machine, where "va_list" is just a typedef for "char *",
the first vfprintf() modifies a *copy* of (the entire value of)
ap, instead of the (single) array element ap[0]. The second
vfprintf() gets another new, fresh copy, and it all works.

In other words, this bug-ridden modified message() function works
on some machines, and fails on others -- just what one can expect
of code with undefined behavior.

One fix, and the only one available in C89, is to rewrite
message() itself:

int message(const char *fmt, ...) {
va_list ap;
int ret;
extern FILE *logfile;

va_start(ap, fmt);
ret = vfprintf(stdout, fmt, ap);
va_end(ap);
va_start(ap, fmt);
ret = vfprintf(logfile, fmt, ap); /* OK this time */
va_end(ap);
return ret;
}

The first va_end cleans up, and the second va_start "resets" the
array contents, so that the second vfprintf() works.

This is all well and good until we go to write the missing vmessage()
function, a la fprintf() and vfprintf(). Our mesage() has the
actual "fmt, ..." parameters and can use va_start() twice -- but
vmessage() should read something like:

int vmessage(const char *fmt, va_list ap) {
extern FILE *logfile;

(void) vfprintf(stdout, fmt, ap);
return vfprintf(logfile, fmt, ap); /* OOPS! WRONG! BUG! */
}

Here we have the same problem as last time: we destroy the values
in ap[0].__ni, ap[0].__nf, and so on, so that the second vfprintf()
prints junk. But in C89, there is no way to fix it. We can
instead require two copies of the va_list:

int ugly_vmessage(const char *fmt, va_list ap1, va_list ap2) {
extern FILE *logfile;

(void) vfprintf(stdout, fmt, ap1);
return vfprintf(logfile, fmt, ap2); /* OK now */
}

which requires the caller to do two va_start()s. This is the only
thing we *can* do. In C99, however, we have the new va_copy macro,
so we can fix it the other way:

int vmessage(const char *fmt, va_list ap) {
extern FILE *logfile;
va_list copy;

va_copy(copy, ap);
(void) vfprintf(stdout, fmt, copy);
va_end(copy); /* see note below */

return vfprintf(logfile, fmt, ap); /* OK now */
}

Now vmessage() still destroys its "va_list" argument on the way
out, but it first makes a copy and uses that to print to stdout.
Note, my C99 draft standard implies that the above va_end() call
is required -- that a va_copy() has a sort of implied va_start()
-- although this is just from an example and not from the text
itself. I would check the final standard before assuming this is
correct.

Without seeing all the code for gsScanfV() and its caller(s),
I cannot say whether a va_copy() is required. (My crystal ball
is a bit cloudy.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (4039.22'N, 11150.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
 
Reply With Quote
 
Chris Croughton
Guest
Posts: n/a
 
      04-06-2005
On 5 Apr 2005 17:25:21 GMT, Chris Torek
<(E-Mail Removed)> wrote:

> In article <(E-Mail Removed)>
> Chris Croughton <(E-Mail Removed)> wrote:


>>It doesn't seem to be. C99 says that you can pass it as an argument to
>>a function, and to va_start/va_arg/va_end/va_copy, but the existence of
>>va_copy() would seem to imply that just assigning it is not a defined
>>behaviour

>
> This is correct. In particular, on the machine he has, the contents
> of <stdarg.h> include something like this:
>
> typedef struct __builtin_va_list_struct_type va_list[1];
>
> struct __builtin_va_list_struct_type {
> int __ni; /* number of saved int regs */
> int __i[__VA_MAXI]; /* and their values */
> int __nf; /* number of saved FPU regs */
> float __f[__VA_MAXF]; /* and their values */
> int *__rest; /* other values (on stack) */
> };


Aha! Yes, that explains why assignment doesn't work. So would

typedef unsigned char va_list[128];

for that matter.

>>(gcc/glibc seems to accept it, but that doesn't imply that
>>anything else would).

>
> The machine on which va_copy is a simple copy, by contrast, has this
> in its <stdarg.h>:
>
> typedef char *va_list;


Yes, that (or similar with void*) is the first implementation of varargs
I encountered, many years ago...

>>> I could solve the above compilation error by changing the "va_list
>>> arglist " to "va_list ap" in the gsScanfV() function parameters and
>>> removing the assignmet line (va_list ap = arglist.

>
> This is a good solution *provided* it is OK to destroy the va_list
> object whose element-address has been passed (courtesy, again, of
> The Rule). Consider for instance:


[big snip describing why va_copy is needed]

Thanks! I've never seen such a comprehensive (and comprehensible)
description of the problem and solution before.

> Without seeing all the code for gsScanfV() and its caller(s),
> I cannot say whether a va_copy() is required. (My crystal ball
> is a bit cloudy.)


It's a pity va_copy() wasn't in C89...

Thanks for the explanations...

Chris C
 
Reply With Quote
 
grid
Guest
Posts: n/a
 
      04-06-2005
> This is correct. In particular, on the machine he has, the contents
> of <stdarg.h> include something like this:
>
> typedef struct __builtin_va_list_struct_type va_list[1];
>
> struct __builtin_va_list_struct_type {
> int __ni; /* number of saved int regs */
> int __i[__VA_MAXI]; /* and their values */
> int __nf; /* number of saved FPU regs */
> float __f[__VA_MAXF]; /* and their values */
> int *__rest; /* other values (on stack) */
> };
>
>>(gcc/glibc seems to accept it, but that doesn't imply that
>>anything else would).

>


I tried to see the <stdarg.h> file for my implementation (its glibc on
x86_64 for Linux ),and I dont seem to find the builtin_va_list_struct_type.
So I wrote a simple program,and I am pasting the preprocessed output.The
<stdarg.h> for my implementation is similar to this
http://www.elrincondelprogramador.co...cs/54/stdarg.h .

Program :
--------
#include<stdio.h>
#include<stdarg.h>

void myprintf(const char *fmt,...)
{
va_list ap;
int a,b;

va_start(ap,fmt);
a = va_arg(ap,int);
b = va_arg(ap,int);
va_end(ap);
printf("a == %d \n b == %d \n",a ,b);
}

int main()
{
myprintf("%d %d\n",1,2);
return 0;
}

Preprocessed Output (Edited the irrelevent parts ):
---------------------------------------------------

# 1 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 1 3
# 43 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 3
typedef __builtin_va_list __gnuc_va_list;

# 2 "test.c" 2
# 1 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 1 3
# 110 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 3
typedef __gnuc_va_list va_list;
# 3 "test.c" 2

void myprintf(const char *fmt,...)
{
va_list ap;
int a,b;
__builtin_stdarg_start((ap),fmt);
a = __builtin_va_arg(ap,int);
b = __builtin_va_arg(ap,int);
__builtin_va_end(ap);
printf("a == %d \n b == %d \n",a ,b);

}
int main()
{
myprintf("%d %d\n",1,2);
return 0;
}

I am just invoking usin the default options, and hence should not invoke
with -C99 flags , moreover programs are here are not compiled with
-C99 flag as yet.So probably the implementation provides a va_copy
implementation for the C89 mode too.The compiler is gcc version 3.2.3
20030502 (Red Hat Linux 3.2.3-34) for the RHEL.

I would like to see the actual implementations of the __builtin_* types
, but could not find in the include dir.

Thanks,
 
Reply With Quote
 
Chris Croughton
Guest
Posts: n/a
 
      04-06-2005
On Wed, 06 Apr 2005 19:22:22 +0530, grid
<(E-Mail Removed)> wrote:

> I tried to see the <stdarg.h> file for my implementation (its glibc on
> x86_64 for Linux ),and I dont seem to find the builtin_va_list_struct_type.


If it's builtin it probably means that it's a type the compiler knows
about without any headers (like it does int, double etc.). As it
happens I've just build GCC 3.4.3 so I have the source tree lying
around...

> So I wrote a simple program,and I am pasting the preprocessed output.The
> <stdarg.h> for my implementation is similar to this
> http://www.elrincondelprogramador.co...cs/54/stdarg.h .
>
> Preprocessed Output (Edited the irrelevent parts ):
> ---------------------------------------------------
>
> # 1 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 1 3
> # 43 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 3
> typedef __builtin_va_list __gnuc_va_list;


There it is. On this system it seems to be __builtin_va_list instead.
Looking at the GCC Source I find that it is indeed recognised as an
internal type (I'm not going to dig and work out what exactly).

> I would like to see the actual implementations of the __builtin_* types
> , but could not find in the include dir.


You won't find them in the headers, they are internal to the compiler.
You could download the compiler source (go for gcc-core, it's a lot
smaller than the whole thing) and try to work out what that is doing...

The point is that the internals of such macros are only of interest
academically. If you try to use such undocumented and implementation
specific things then you will run into trouble as soon as the
implementers change them, which they are quite likely to do. And being
macros you won't gain anything by expanding them by hand anyway...

Chris C
 
Reply With Quote
 
Chris Torek
Guest
Posts: n/a
 
      04-08-2005
[I wrote, in part:]
>> This is correct. In particular, on the machine he has, the contents
>> of <stdarg.h> include something like this:
>>
>> typedef struct __builtin_va_list_struct_type va_list[1];
>>
>> struct __builtin_va_list_struct_type {
>> int __ni; /* number of saved int regs */
>> int __i[__VA_MAXI]; /* and their values */
>> int __nf; /* number of saved FPU regs */
>> float __f[__VA_MAXF]; /* and their values */
>> int *__rest; /* other values (on stack) */
>> };


In article <q1S4e.38$(E-Mail Removed)>
grid <(E-Mail Removed)> wrote:
>I tried to see the <stdarg.h> file for my implementation (its glibc on
>x86_64 for Linux), and I dont seem to find the builtin_va_list_struct_type.


Aha. I thought you probably had some other compiler, and were
most likely on a PowerPC-like machine. (Just goes to show that
my crystal ball is cloudier than I would like. )

>So I wrote a simple program, and I am pasting the preprocessed output. ...


You will not find the expansion of the structure here. The reason
is simple enough: the GNU GCC folks have done the sensible thing,
and put the important contents of <stdarg.h> -- which must match
the compiler's code for __builtin_stdarg_start, and is therefore
known to the compiler -- inside the compiler. (See how neat it
is? All the magic is inside the compiler: the definition of the
structure itself, the code that fills it in, and the code that uses
it, are all in the same place, and hence cannot get out of sync
with each other.)

Unfortunately (?), this has the side effect that the magic is now
invisible to the casual programmer. The only place to see it all
happen is in the source code to the compiler.

>I would like to see the actual implementations of the __builtin_*
>types, but could not find in the include dir.


Alas, they are off-topic here, and the on-topic groups (gnu.gcc.*)
are mostly dead on my news server (gnu.g++.bug gets a lot of spam
and an occasional actual bug report).
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (4039.22'N, 11150.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
dotnet doubt can any body clarify my doubt challa462@gmail.com ASP .Net 0 08-22-2012 06:02 AM
doubt about doubt Bob Nelson C Programming 11 07-30-2006 08:17 PM
doubt Ajith Nair ASP .Net 6 03-29-2005 01:25 PM
doubt about namespace google manasa sreenivas via .NET 247 ASP .Net 1 05-15-2004 03:05 PM
Doubt to go .Net technology Igbal ASP .Net 1 11-17-2003 06:56 AM



Advertisments