problems with stdarg.h and own printf

Programming, for all ages and all languages.
Post Reply
FlashBurn
Member
Member
Posts: 313
Joined: Fri Oct 20, 2006 10:14 am

problems with stdarg.h and own printf

Post by FlashBurn »

I have the following stdarg.h:

Code: Select all

typedef uint8t *va_list;

#define va_start(ap, v)   ((void) (ap = (va_list) &v + sizeof(v)))
#define va_arg(ap, type) (*((type *)(ap))++)
#define va_end(ap) ((void) (ap = 0))
This is my printf:

Code: Select all

void printf(char *format, ...) {
	va_list argptr;
	char s[34], c, *str;
	int num;
	
	va_start(argptr,format);
	
	while(*format != '\0') {
		if(*format != '%') {
			putchar(*format);
		} else {
			*format++;
			switch(*format) {
			case '%':
				putchar('%');
				break;
			case 'b':
				num= va_arg(argptr,int);
				itoa(s,num,2,PRINTF_SMALL);
				printf(s);
				break;
			case 'c':
				c= va_arg(argptr,char);
				putchar(c);
				break;
			case 's':
				str= va_arg(argptr,char *);
				printf(str);
				break;
			case 'x':
				num= va_arg(argptr,int);
				itoa(s,num,16,PRINTF_SMALL);
				printf(s);
				break;
			case 'X':
				num= va_arg(argptr,int);
				itoa(s,num,16,PRINTF_BIG);
				printf(s);
				break;
			case 'd':
				num= va_arg(argptr,int);
				itoa(s,num,10,PRINTF_SMALL);
				printf(s);
				break;
			default:
				putchar(--*format);
				putchar(++*format);
				break;
			}
		}
		*format++;
	}
	
	va_end(argptr);
}
Whenever I use va_arg I get this error: "lvalue required as increment operand". I have an old kernel which uses the same macros and it does compile, but this code doesn´t. I use gcc 4.2.1 (for both).
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post by Solar »

Curious. I'm sure the answer is simple, I just can't find it ad hoc.

Simplified:

Code: Select all

typedef int * va_list;

#define va_start( ap, v ) ((void) (ap = (va_list) &v + sizeof(v)))
#define va_end(ap) ((void) (ap = 0))

#ifdef ORIGINAL
#define va_arg(ap, type) (*((type *)(ap))++)
#else
#define va_arg(ap, type) (*(ap)++)
#endif

int printf( char * format, ... )
{
    va_list argptr;
    va_start( argptr, format );
    int num = va_arg( argptr, int );
    return num;
}

int main()
{
    return printf( "foo", 42 );
}
If compiled with -DORIGINAL, the error appears. If compiled without (i.e., without the cast in va_arg()), it compiles - but returns 20 instead of the expected 42.
Every good solution is obvious once you've found it.
FlashBurn
Member
Member
Posts: 313
Joined: Fri Oct 20, 2006 10:14 am

Post by FlashBurn »

I solved the problem by using other macros:

Code: Select all

#define va_start(ap,v) ap=((uint8t*)&v)+4
#define va_arg(ap,type) (ap+=sizeof(type),*(type*)((void*)ap-sizeof(type)))
#define va_end(ap) ((void)0)
User avatar
bluecode
Member
Member
Posts: 202
Joined: Wed Nov 17, 2004 12:00 am
Location: Germany
Contact:

Post by bluecode »

A gcc crosscompiler already comes with an implementation of stdarg.h. I would use that instead of your own.
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

Straight from voix library.h:

Code: Select all


// Vararg handling, stdarg.h style, let GCC handle the nastiness..
typedef __builtin_va_list va_list;
#define va_start __builtin_va_start
#define va_end __builtin_va_end
#define va_arg __builtin_va_arg
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post by Solar »

Generally speaking, there is no standard-compliant way to do varargs parsing, but every compiler must provide a way to do so. Thus, it's quite OK to rely on the compiler builtins, as mystran showed above.
Every good solution is obvious once you've found it.
Post Reply