Topic: va_start() clarification please


Author: "Igor Stauder" <igor.stauder@swh.sk>
Date: 2000/03/01
Raw View
Thanks all for your answers. Your replays were closely related but
unfortunately not exactly what I was trying to find out. I'd like to
emphasize that my question is about ***va_start()*** not about
va_arg() etc, although it might seem to be almost the same.

C++ standard says:
"The parameter parmN is the identifier of the rightmost parameter in the
variable parameter list of the function definition (the one just before
the ...)."

+++ This sentence clearly states that parmN is a "real" parameter with a
known
type. It means that Section 5.2.2 p7 doesn't apply to it. This section is
specifying default argument promotions for the arguments "matched" by the
ellipsis (...) i.e. the arguments AFTER the parmN.

C++ standard says:
 "If the parameter parmN is declared with a function, array, or reference
 type, or ..."

+++ Let's presume that parmN is NOT declared with a function, array, or
reference type. Then the sentence can be simplified as follows:

 "If the parameter parmN is declared with a type that is not compatible with
 the type that results when passing an argument for which there is no
 parameter, the behavior is undefined."

+++ This is still quite complicated so I use an example:

 // function with the variable parameter list
void FVA(int myParmN, ...)
{
     // do whatever is necessary
}
-------------
  // call the function with variable parameter list
int i = 1;
float f = 5.0;
FVA(i, f);

+++ ... the parameter parmN is declared with a type ...  the type is 'int'
(let's call it T1)
+++ ... an argument for which there is no parameter ... is 'f', with the
type 'float'
+++ ... the type that results when passing an argument for which there is no
parameter ... is 'double' (float promoted to double, see section 5.2.2 p7
and 4.6 p1) (let's call it T2)

So my understanding is that using the preceding example the standard says:
If T1 is not compatible with T2 the behavior is undefined.

OK and finally here are my questions:
When is T1 not compatible with T2 ?
If T1 is not declared with a function, array, or reference type is it even
possible that T1 is incompatible with T2?
What 'compatible' means in this context ?

Thanks for your time,
Igor.
igor.stauder@swh.sk


James Kuyper <kuyper@wizard.net> wrote in message news:
> Igor Stauder wrote:
> >
> > Section 18.7 [lib.support.runtime] paragraph 3:
> > The restrictions that ISO C places on the second parameter to the
va_start()
> > macro in header
> > <stdarg.h> are different in this International Standard. The parameter
parmN
> > is the identifier of the
> > rightmost parameter in the variable parameter list of the function
> > definition (the one just before the ...).
> > If the parameter parmN is declared with a function, array, or reference
> > type, or with a type that is not compatible
> > with the type that results when passing an argument for which there is
no
> > parameter, the behavior is
> > undefined.
> >
> > I understand that for example:
> >
> > void
> > FVA(const int& myParmN, ...)
> > {
> >  va_list args;
> >
> >  // undefined - myParmN is a reference type
> >  va_start(args, myParmN);
> >        //  .......
> >  va_end(args);
> >
> >  return;
> > }
> >
> > I would appreciate if somebody could explain (with an example) the
following
> > part:
> > If the parameter parmN is declared ...
> > ... with a type that is not compatible with the type that results when
> > passing
> > an argument for which there is no parameter, the behavior is undefined.
>
> Section 5.2.2 p7 says "When there is no parameter for a given argument,
> ....  If the argument has an itegral or enumeration type that is subject
> to the integral promotions (4.5), or a floating point type that is
> subject ot the floating point promotions (4.6), the value of the
> argument is converted to the promoted type before the call. These
> promotions are referred to as the _default argument promotions_."
>
> You need to use a type compatible with the converted one, to safely
> extract the value using va_arg().
>
> You asked for an example. I'll modify the example given in section
> 7.15.1.4 of the C99 standard, so that it will have undefined behavior
> because of the above rule.
>
> #include <cstdarg>
>
> void f2(int n_ptrs, float array[])
> {
> // do something with the first n_ptrs elements of array
> }
>
> void f1(int n_ptrs, ...)
> {
> va_list ap;
> float array[MAXARGS];
> int ptr_no = 0;
> if (n_ptrs > MAXARGS)
> n_ptrs = MAXARGS;
> va_start(ap, n_ptrs);
> while (ptr_no < n_ptrs)
> array[ptr_no++] = va_arg(ap, float);
> va_end(ap);
> f2(n_ptrs, array);
> }
>
> void f3()
> {
> float x=1.0, y=2.0, z=3.0;
> f1(3, x, y, z);
> }
>
> f3() passes floats arguments to f1(), and f1 extracts them as floats, so
> a naive user might expect it to work. However, since there are no
> parameter names corresponding to the x, y, and z arguments, the default
> argument promotions occur, promoting them to double. On most
> implementations, 'float' is not compatible with 'double', so on such
> implementations, f1() allows undefined behavior.
>
> For instance, on one implementation I'm familiar with, float is 4 bytes
> and double is eight bytes. f1() would try to interpret the first four
> bytes of x as a 'float', and put them in array[0], and then try to
> interpret the next four bytes as a different 'float', and store it in
> array[1]. However, the exact details of how it will fail depend upon the
> argument passing mechanism, which varies from implementation to
> implementation.






---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: =?ISO-8859-1?Q?J=F6rg?= Barfurth <joerg.barfurth@germany.sun.com>
Date: 2000/03/02
Raw View

>>>>>>>>>>>>>>>>>> Urspr   ngliche Nachricht <<<<<<<<<<<<<<<<<<

Am 01.03.00, 07:20:57, schrieb "Igor Stauder" <igor.stauder@swh.sk> zum
Thema Re: va_start() clarification please:


> Thanks all for your answers. Your replays were closely related but
> unfortunately not exactly what I was trying to find out. I'd like to
> emphasize that my question is about ***va_start()*** not about
> va_arg() etc, although it might seem to be almost the same.

> C++ standard says:
> "The parameter parmN is the identifier of the rightmost parameter in the
> variable parameter list of the function definition (the one just before
> the ...)."

> +++ This sentence clearly states that parmN is a "real" parameter with a
> known
> type. It means that Section 5.2.2 p7 doesn't apply to it. This section is
> specifying default argument promotions for the arguments "matched" by the
> ellipsis (...) i.e. the arguments AFTER the parmN.

> C++ standard says:
>  "If the parameter parmN is declared with a function, array, or reference
>  type, or ..."

> +++ Let's presume that parmN is NOT declared with a function, array, or
> reference type. Then the sentence can be simplified as follows:

>  "If the parameter parmN is declared with a type that is not compatible
with
>  the type that results when passing an argument for which there is no
>  parameter, the behavior is undefined."

This basically means: The type of parmN is one that could itself be
safely extracted using va_start.

AFAICS the reason for this is, that va_start(args,parmN) might be
#defined as something like
 ((args) = (va_list)(&parmN + 1))
[va_list usually is some kind of pointer type]. This is possible, if all
the promoted [*] types have the same alignment requirements. Using this
va_start() with a type of the wrong size and/or alignment requirements
(e.g. short,char) would be a problem.

Or it might (effectively) be:
 (args = &parmN, (void)va_arg(args, typeof(parmN))
If parmN is of a type not eligible to be used with va_arg this may
produce an invalid va_list.

Q: Is this more restrictive than in C90 ?

[*] 'promoted type' here means
    'the type that results when passing an argument for which there is no
parameter
     to a function with an ellipsis in its parameter list'



> +++ This is still quite complicated so I use an example:

>  // function with the variable parameter list
> void FVA(int myParmN, ...)
> {
>      // do whatever is necessary
> }

int is the same as its promoted type [*], so FVA is OK regardless.

> -------------
>   // call the function with variable parameter list
> int i = 1;
> float f = 5.0;
> FVA(i, f);

> +++ ... the parameter parmN is declared with a type ...  the type is
'int'
> (let's call it T1)

agreed

> +++ ... an argument for which there is no parameter ... is 'f', with the
> type 'float'

We are not talking about any specific argument to this specific function.
Rather we look at the same call as if parmN weren't an explicitly
declared parameter, but part of the ellipsis.

> +++ ... the type that results when passing an argument for which there is
no
> parameter ... is 'double' (float promoted to double, see section 5.2.2 p7
> and 4.6 p1) (let's call it T2)

+++ ... the type that results when passing an _'int'_as_ argument for
which there is no
parameter ... is 'int' (
(let's call it T1pro)

> So my understanding is that using the preceding example the standard
says:
> If T1 is not compatible with T2 the behavior is undefined.

So my understanding is that using the preceding example the standard
says:
If T1 is not compatible with T1pro the behavior is undefined.

> OK and finally here are my questions:
> When is T1 not compatible with T2 ?
> If T1 is not declared with a function, array, or reference type is it
even
> possible that T1 is incompatible with T2?

Hope I answered that. Its not T2 you have to worry about.

> What 'compatible' means in this context ?

I don't have the standard here, but I read 'compatible' to mean:
  'has the same object representation and alignment requirements'.
On a machine where numeric_limits<unsigned short> ==
numeric_limits<unsigned int>, 'unsigned short' would be allowed (compatible
with 'unsigned int'), if it has the same alignment requirements as the latter.

Portably you could e.g. use int,long,double, but not char,short,float.
There may be some restrictions with pointer types (other than 'void*' and
'char*') as well.

--
J   rg Barfurth


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Ronny <ronald_f@myremarq.com>
Date: 2000/02/25
Raw View
In article <88tkri$l6f$1@galaxy.mchh.siemens.de>, "Igor Stauder"
<igor.stauder@swh.sk> wrote:
>I would appreciate if somebody could explain (with an example)
the following
>part:
>If the parameter parmN is declared ...
>.... with a type that is not compatible with the type that
results when
>passing
>an argument for which there is no parameter, the behavior is
undefined.

For example, if you have void FVA(char myParamN,...). If you
would pass an argument of type char, and there were no formal
parameter defined for that argument (due to ellipsis notation),
char would be passed as int, so it were not compatible with the
char referred to by va_start.

I am, however, surprised about the existence of this rule. After
all, the compiler *knows* for myParamN that the first parameter
is expected, so it has no need converting it to an int....

Ronald Fischer <ronald_f@myremarq.com>
http://profiles.yahoo.com/ronny_fischer/
http://fusshuhn.ourfamily.com/cppincomp.html

* Sent from RemarQ http://www.remarq.com The Internet's Discussion Network *
The fastest and easiest way to search and participate in Usenet - Free!

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Igor Stauder" <igor.stauder@swh.sk>
Date: 2000/02/24
Raw View
Section 18.4 [lib.support.runtime] paragraph 3:
The restrictions that ISO C places on the second parameter to the va_start()
macro in header
<stdarg.h> are different in this International Standard. The parameter parmN
is the identifier of the
rightmost parameter in the variable parameter list of the function
definition (the one just before the ...).
If the parameter parmN is declared with a function, array, or reference
type, or with a type that is not compatible
with the type that results when passing an argument for which there is no
parameter, the behavior is
undefined.

I understand that for example:

void
FVA(const int& myParmN, ...)
{
 va_list args;

 // undefined - myParmN is a reference type
 va_start(args, myParmN);
       //  .......
 va_end(args);

 return;
}

I would appreciate if somebody could explain (with an example) the following
part:
If the parameter parmN is declared ...
... with a type that is not compatible with the type that results when
passing
an argument for which there is no parameter, the behavior is undefined.

Thank you in advance,
Igor.



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Ken Hagan" <K.Hagan@thermoteknix.co.uk>
Date: 2000/02/24
Raw View
I'm guessing, but in the case of "short"...

If the parameter parmN is declared with a type (short) that is
not compatible with (not the same size as) the type that results
(int) when passing an argument for which there is no parameter
(anything in the ... zone), the behaviour is undefined (since the
va_xxx macros typically use sizeof to walk up the stack).

"Igor Stauder" <igor.stauder@swh.sk> wrote in message
news:88tkri$l6f$1@galaxy.mchh.siemens.de...
> I would appreciate if somebody could explain (with an example) the
> following part:
> If the parameter parmN is declared ...
> ... with a type that is not compatible with the type that results when
> passing
> an argument for which there is no parameter, the behavior is undefined.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James Kuyper <kuyper@wizard.net>
Date: 2000/02/25
Raw View
Igor Stauder wrote:
>
> Section 18.4 [lib.support.runtime] paragraph 3:

That's section 18.7, not 18.4.

> The restrictions that ISO C places on the second parameter to the va_start()
> macro in header
> <stdarg.h> are different in this International Standard. The parameter parmN
> is the identifier of the
> rightmost parameter in the variable parameter list of the function
> definition (the one just before the ...).
> If the parameter parmN is declared with a function, array, or reference
> type, or with a type that is not compatible
> with the type that results when passing an argument for which there is no
> parameter, the behavior is
> undefined.
>
> I understand that for example:
>
> void
> FVA(const int& myParmN, ...)
> {
>  va_list args;
>
>  // undefined - myParmN is a reference type
>  va_start(args, myParmN);
>        //  .......
>  va_end(args);
>
>  return;
> }
>
> I would appreciate if somebody could explain (with an example) the following
> part:
> If the parameter parmN is declared ...
> ... with a type that is not compatible with the type that results when
> passing
> an argument for which there is no parameter, the behavior is undefined.

Section 5.2.2 p7 says "When there is no parameter for a given argument,
....  If the argument has an itegral or enumeration type that is subject
to the integral promotions (4.5), or a floating point type that is
subject ot the floating point promotions (4.6), the value of the
argument is converted to the promoted type before the call. These
promotions are referred to as the _default argument promotions_."

You need to use a type compatible with the converted one, to safely
extract the value using va_arg().

You asked for an example. I'll modify the example given in section
7.15.1.4 of the C99 standard, so that it will have undefined behavior
because of the above rule.

#include <cstdarg>

void f2(int n_ptrs, float array[])
{
 // do something with the first n_ptrs elements of array
}

void f1(int n_ptrs, ...)
{
 va_list ap;
 float array[MAXARGS];
 int ptr_no = 0;
 if (n_ptrs > MAXARGS)
  n_ptrs = MAXARGS;
 va_start(ap, n_ptrs);
 while (ptr_no < n_ptrs)
  array[ptr_no++] = va_arg(ap, float);
 va_end(ap);
 f2(n_ptrs, array);
}

void f3()
{
 float x=1.0, y=2.0, z=3.0;
 f1(3, x, y, z);
}

f3() passes floats arguments to f1(), and f1 extracts them as floats, so
a naive user might expect it to work. However, since there are no
parameter names corresponding to the x, y, and z arguments, the default
argument promotions occur, promoting them to double. On most
implementations, 'float' is not compatible with 'double', so on such
implementations, f1() allows undefined behavior.

For instance, on one implementation I'm familiar with, float is 4 bytes
and double is eight bytes. f1() would try to interpret the first four
bytes of x as a 'float', and put them in array[0], and then try to
interpret the next four bytes as a different 'float', and store it in
array[1]. However, the exact details of how it will fail depend upon the
argument passing mechanism, which varies from implementation to
implementation.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]