stdarg.h / va_list & Co

Go To Last Post
16 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void start ( char *ptr, uint8_t s_words, uint8_t num,... ) {
  va_list uk_arg;
  char *p;
  init_sk ( s_words, ptr, num );
  va_start ( uk_arg, num );
  while (( p = va_arg ( uk_arg, char * )) != NULL ) {
#if 1
// this code doesn't work
    uint8_t sw = va_arg ( uk_arg, uint8_t );
    uint8_t n  = va_arg ( uk_arg, uint8_t );
#else
// this works
    uint8_t sw = va_arg ( uk_arg, uint16_t );
    uint8_t n  = va_arg ( uk_arg, uint16_t );
#endif
    init_sk ( sw, p, n );
  }
  va_end ( uk_arg );
}
 // that is actual call
 start ( t1, 8, 1, t2, 8, 2, NULL );
 

My qwestion is why it does what it does?
Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's supposed to work. If something doesn't work, I think
we need some minimal compilable project that demonstrates the
problem in order to analyze it. After all, this does work
e. g. in the vfprintf() implementation.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One of my compilation flags is -mint8 which makes sizeof NULL = 1.
The solution for my example is ( char * )NULL
Why did uint16_t work I don't know.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

> One of my compilation flags is -mint8

Umm, sorry, then all bets are off. This option is "Use at
your own risk" only, and many parts of the library are
known to be incompatible with it. It doesn't surprise me
that this option might also cause surprises with standard
header files...

Seriously, that option is only good (if at all) for very tiny
applications that don't use much more of the library than the
initial startup code.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

>Seriously, that option is only good (if at all) for very tiny
>applications that don't use much more of the library than the
>initial startup code.
This option does make code faster and it worth debuging when speed is critical.
so I disagree with your statement about "tiny applications".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sure it makes code faster, but it is known to violate the C standard,
and as such it is fundamentally incompatible with the standard C
library. Due to that, sorry, there are no promises whatsoever about
its usefulness. The original authors of this option even once
intented to completely drop it due to the unforseeable side-effects it
might have (you just noticed one of them, and nobody can tell you how
many of that subtle kind might be there). So if you insist on this
being `fixed', and file a GCC bug report for it, you'll risk the GCC
maintainers might even decide to drop it.

I fully agree that AVR-GCC's 8-bit awareness could be better, but
that's really a long story, and nothing that can be solved within an
afternoon (alas). -mint8 is not a solution to it either, it's (at the
best) a workaround that might be useful for applications that don't
depend much on the library, and at worst it could be considered just a
bad hack that should have never happened.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, yes, yes!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

By the way, it was several discussions about optimization and pragmas. So -mint8 can be a valuable solution for some files in the project.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I seem to remember participating in those discussions with you. You were discouraged from using -mint8 at that point as well.

Some alternatives were proposed for avoiding the unnecessary 8-bit to 16-bit promotions. As I recall, most of the trouble was the way in which the compiler treated your constants when they were used as operands.

It was suggested that you could modify the source code so that those constants were assigned into temporary, local 8-bit "scratch" register variables, and then the scratch variables could be used instead of the original constants in the following operations.

The optimizer would take care of ensuring that no extra code was actually generated by the explicit assignments, and the integer promotion wouldn't happen because both operands were explicitly of the same type.

But you didn't want to have to modify your source code, so you chose to use -mint8 instead, despite being warned of the possibility of unwanted side effects.

As another alternative that wasn't available back when the original discussion was raging, you may wish to try compiling your code (without -mint8 and any associated backflips your code may now contian to accommodate it) under gcc 4.1 and see if it does a better job of avoiding the 16-bit promotions.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When the new WinAVR realise will come I'll try it. For now -mint8 allows me to have some extra execution time, so I have to spend more development time instead ;)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

new troubles

#include 
#include 

void test ( uint8_t *b, ... ) {
  va_list		list;
  uint8_t   x;

  va_start( list, b );
  while (( x = va_arg ( list, uint8_t )) != 0 ) *b += x;
  va_end(list);

}

int main ( void ) {
  uint8_t sum;

  sum = 0;
  test ( &sum, 5, 4, 3, 0 );
  return sum;
}

compilation options:
avr-gcc.exe -mmcu=at90can128 -Wall -gdwarf-2 -std=gnu99 -DF_CPU=16000000UL -O0 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT oc.o -MF dep/oc.o.d -c
WARNING:
../oc.c: In function `test':
../oc.c:9: warning: `uint8_t' is promoted to `int' when passed through `...'
../oc.c:9: warning: (so you should pass `int' not `uint8_t' to `va_arg')
../oc.c:9: note: if this code is reached, the program will abort

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What's troublesome here? It's the only way it can work with
variadic arguments. As the compiler has no type information
for the parameters passed as ..., it has to apply standard
argument promotion rules.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dl8dtl wrote:
What's troublesome here?
As the compiler has no type information for the parameters passed as ..., it has to apply standard
argument promotion rules.

x = va_arg ( list, uint8_t )

uint8_t is a known type information for the compiler. Is it not?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is, but it cannot be passed as part of a variadic
argument list (per the C standard). The compiler told
you clearly. If you want a not-so-clear description,
have a look into the C standard.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can someone tell my why I am getting garbage in format with this:

int print (const char *format, ...)  /* Our main entry */
{
	int nr_of_chars;
	va_list ap;
/**  	char *p;
	p=format;
	put_char('p');
	put_char('r');
	put_char('i');
	put_hex(p[0]);
	put_hex(p[1]);
	put_hex(p[2]);
	put_hex(p[3]);
	put_hex(p[4]);
	put_hex(p[5]);
	put_hex(p[6]);
	put_char('d');
	while(*format)
		put_char(*format++);
	return 0;	*/
	va_start (ap, format);  /* assign to ap a pointer to the first argument */
	nr_of_chars = decode_printf((char *)format, ap);
	va_end (ap);  /* Variable argument end */
	return (nr_of_chars); /* According to ANSI */
}

GCC was configured with:
../configure --target=avr --prefix=/usr/local --disable-nls --enable-languages=c
Thread model: single
gcc version 4.1.0

I include stdarg in a header file inclued with this function.

Here is the compiler call:
avr-gcc -Os -Wa,-a=print.x -fno-zero-initialized-in-bss -mno-tablejump -Wall -g -mmcu=atmega32 -I ../../common -I . -Os -c ../../common/print.c -o print.o

If I uncomment out the put_hex lines (which basically does
printf("%04x"), I get:
pri001400460001000000000001FF81d
So I am getting 0x14 0x46 0x1 0x0 0x0 0x1 0xFF81
When I call it with

print("Phello\n");

Any clues?
Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Doh! I forgot about the AVR having different instructions for program space and the need to use PSTR. Sometimes I hate the AVR architecture.

The first problem is not using PSTR in the call to print, and the second is not using pgm_read_byte to read the format string.

Cheers!