in reply to XS on 64-bit: Warning from sv_vsetpvf call

Not exactly sure what this "non-conforming" is about... (so I could be wrong), but I think there is no general problem passing around pointers to va_list. It's just that you shouldn't do something like

int printf_handler(const char *format, va_list args) { ... sv_vsetpvf(printf_str, format, &args); ...

with a 64-bit compiler — unless you're using the va_start etc. macros in that same routine, in which case you wouldn't have va_list in its parameter list.  OTOH, something like this is fine

int printf_handler(const char *format, va_list* args) { ... sv_vsetpvf(printf_str, format, args); ...

(of course, you'd then have to pass &args to printf_handler()...)

Note that both versions do work fine on 32-bit.

Consider the following example (admittedly somewhat contrived and in pure C only (no Perl/XS involved) — but this helps keep it simple). First the erroneous version:

#include <stdarg.h> #include <stdio.h> #include <errno.h> void vatest__(char* format, va_list* args) { // this kind of represents sv_vsetpvf() char buf[1024]; vsprintf(buf, format, *args); errno = 0; // "Success" perror(buf); } void vatest_(char* format, va_list args) { // this represents your printf_handler() vatest__(format, &args); } void vatest(char* format, ...) { va_list args; va_start (args, format); vatest_(format, args); va_end (args); } int main (int argc, char *argv[]) { char* msg1 = "foo"; char* msg2 = "bar"; vatest("%s: %s", argv[0], msg1); // two args vatest("%s: %s,%s", argv[0], msg1, msg2); // three args return 0; }

___

$ gcc -Wall vatest.c -o vatest vatest.c: In function `vatest_': vatest.c:17: warning: passing arg 2 of `vatest__' from incompatible po +inter type $ ./vatest Segmentation fault $ file vatest vatest: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), for G +NU/Linux 2.6.4, dynamically linked (uses shared libs), for GNU/Linux +2.6.4, not stripped

However, if you change the middle two routines as mentioned above

... void vatest_(char* format, va_list* args) { vatest__(format, args); } void vatest(char* format, ...) { va_list args; va_start (args, format); vatest_(format, &args); va_end (args); } ...

things are working fine, even on 64-bit:

$ gcc -Wall vatest.c -o vatest $ ./vatest ./vatest: foo: Success ./vatest: foo,bar: Success

(tested with gcc 3.4.6 on 64-bit SUSE Linux, and with gcc 4.0.4 on a 32-bit Debian box)

Replies are listed 'Best First'.
Re^2: XS on 64-bit: Warning from sv_vsetpvf call
by songmaster (Beadle) on Jun 27, 2008 at 04:46 UTC

    Unfortunately I can't change printf_handler()'s arguments — it's a callback routine for the external library, whose API I can't alter.

    My words "non conforming" came from that PDF slideshow I pointed to. Looking at the C99 spec (Draft of May 6, 2005) though, footnote 215 does say you can take the address of a va_list and pass it to other functions, so Perl's API isn't broken as I originally thought. It's not obvious how passing the va_list by value affects the ability to then take the address of the copy, but it certainly seems to.

    Hmm, could I copy the original va_list object using va_copy() and then take the address of the copy? That's not particularly efficient, but efficiency doesn't matter much in this case which is in an error path anyway...

    Ok, so va_copy() isn't defined, but using __va_copy() works on both 32 and 64-bit systems, with no warnings. Of course using __va_copy() isn't going to be portable to other compilers, but I can probably live with a special case for 64-bit Linux.

      va_copy() was defined in C99. When using gcc you can get it with either -std=c99 or -std=gnu99.