In article <MwCGi.105460$(E-Mail Removed)-ops.be>

Punkie <(E-Mail Removed)> wrote:

>Indeed, a good way to do this without rewriting sprintf is

>to let *others* rewrite it.
Indeed.

>Altering the types accepted creates a problem as we cant

>use the [gcc-specific] attribute anymore.
You cannot use this portably anyway, even if all your format

directives match those for printf, since __attribute__ is a

gcc-specific extension (i.e., it does not work very well, or even

at all, in various other C compilers).

If you have "extra" directives, a la syslog() "%m", you have to

work harder. If your extra directives need to access some of

the va_list's variable arguments, you must in fact "rewrite sprintf".

Consider, for instance, the following actual implementation (for

V9 SPARC, with a fairly old version of GCC):

/*__va_ll must be 8 bytes regardless of -mlong32; this avoids a pedwarn()*/

typedef long __va_ll __attribute__((__mode__(__DI__)));

/*

* Pre-V9, we simply had up to 6 values in %o registers, then the rest

* in memory. The V9 convention is different -- it had to change because

* the registers are wider, and someone took the opportunity to fix the

* brokenness: floating point arguments are passed in via the first 16

* %f registers now. Thus, we now have an "argument descriptor" data

* structure.

*

* (Using "long long" for the ni and nd produces shorter code.)

*/

typedef struct __va_data {

__va_ll __va_ni; /* # integers remaining */

__va_ll *__va_ip; /* integer-reg arguments */

__va_ll __va_nd; /* # doubles remaining */

double *__va_dp; /* floating-reg arguments */

__va_ll *__va_rest; /* additional args, if any */

} va_list[1];

/*

* The __builtin_args_info expressions return the number of "registers"

* (if any) worth of fixed arguments. For integer parameters, this is

* simply the number of (widened) integers; for float, double, and long

* double parameters, it is the number of %f registers used, accounting

* for the fact that "double" parameters are passed in an even/odd pair

* (leaving a gap if necessary -- e.g., "void f(float x,double y,...)"

* uses %f0 and <%f2:%f3> for a total of 4 %f registers).

*

* When we invoke __builtin_saveregs() here, it dumps those registers

* that were *not* used up by fixed parameters, integer registers first,

* then %f registers. The latter always starts with an even register

* (to form a pair), even if there were an odd number of fixed "float"

* arguments.

*/

#define va_start(ap, l) __extension__ ({ \

__va_ll *__va_base = __builtin_saveregs(); \

int __va_nfi = __builtin_args_info(0); \

int __va_nff = __builtin_args_info(1); \

if (__va_nfi > 6) \

__va_nfi = 6; \

if (__va_nff > 16) \

__va_nff = 16; \

(ap)->__va_ip = __va_base; \

(ap)->__va_dp = (double *)(__va_base + __va_nfi) ; \

(ap)->__va_ni = 6 - __va_nfi; \

(ap)->__va_nd = 8 - ((__va_nff + 1) / 2); \

(ap)->__va_rest = (__va_ll *)__builtin_next_arg(l); \

})

#define va_end(ap)

/*

* Locate (get the address of) the next integer register. Used for

* integers and pointers (in particular, for the hidden pointer that

* is used to pass aggregates).

*/

#define __va_aireg(ap) \

((ap)->__va_ni <= 0 ? (ap)->__va_rest++ : \

((ap)->__va_ni--, (ap)->__va_ip++))

/*

* Internally, this macro sets __va_p to point to the first byte of the

* object, whatever it is, then extracts the appropriate object.

*

* "Real" parameters are the hardest, because we may need to skip over

* a %f-pair-gap when extracting a long double. Fortunately, the number

* of doubles remaining is odd iff the corresponding parameter skipped

* a %f register pair.

*

* Integer and aggregate parameters are easy, but since we are locating

* the bytes of the integer in question, we need to account for our

* big-endian offset: an int or unsigned int is in the last four, not

* the first four, bytes of the 8-byte integer register value as stored

* in memory.

*/

#define va_arg(ap, ty) __extension__ ({ \

void *__va_p; \

const int __vaclass = __builtin_classify_type(*(ty *)0); \

if (__vaclass >= __record_type_class) \

__va_p = *(void **)__va_aireg(ap); \

else if (__vaclass == __real_type_class) { \

const int __va_n = (sizeof(*(ty *)0) + 7) / 8; \

if (__va_n > 1 && (ap)->__va_nd & 1) \

(ap)->__va_nd--, (ap)->__va_dp++; \

if (((ap)->__va_nd -= __va_n) >= 0) { \

__va_p = (ap)->__va_dp; \

(ap)->__va_dp += __va_n; \

} else { \

__va_p = (ap)->__va_rest; \

(ap)->__va_rest += __va_n; \

} \

} else { \

/* oddly, we get better code with two statements here */ \

__va_p = __va_aireg(ap); \

__va_p = (char *)__va_p + 8 - sizeof(*(ty *)0); \

} \

*(ty *)__va_p; \

})

Here, substituting or removing a parameter is pretty tricky,

especially if the substitute has a different size and/or type from

the original (e.g., replacing a 4-byte "int" with an 8-byte pointer,

or pointer with floating-point value). You need to know which of

the three regions the parameter to be substituted or removed falls

into, and for substitutions, whether the replacement goes in the

same region or not. This information has been removed (from the

structure to which "ap" points) by the time you have used va_arg()

to extract the original value.

Another implementation is much simpler:

#define va_arg(ap, type) \

((type *)(ap += sizeof(type)))[-1]

(where va_start(ap, last) just sets "ap" to point to the right

place in the [single] stack that this implementation uses). Here

substitutions or deletions are usually just a matter of poking the

right value into place, or doing a memmove(). Finding the right

location is easy; finding the size for memmove() is harder but

not nearly as hard as the V9 SPARC version.

Nothing requires the implementation of va_start, va_arg, and so on

to look like one of the two above, but they are the two common

methods I have seen. (Most or all of the tricky stuff can be, and

in my opinion should be, done internally by the compiler, allowing

the following compiler-specific -- but not machine-specific --

variant of <stdarg.h>:

#define va_start(ap, last) __builtin_va_start(ap)

#define va_arg(ap, ty) __builtin_va_arg(ap, ty)

#define va_end(ap) __builtin_va_end(ap)

For some reason, not too many people seem to agree with me on

this one.

)

--

In-Real-Life: Chris Torek, Wind River Systems

Salt Lake City, UT, USA (40°39.22'N, 111°50.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.