Search code examples
cgccabigcc4.7

How to pass a variable list of parameters to vprintf in a portable way?


I have a code that receives binary array of 32-bit values from a device and prints them with vsprintf, like this:

void print_stuff(int32_t *p, const char *format)
{
    vprintf( format, (va_list)p);
}

(this is simplified; it's ensured that the values match the format, etc.)

Basically this relies on that in normal x86 va_list is just a pointer (or array). This compiles without warnings.

Now I need to port this to ARM (arm-linux-gnueabihf) and x64, and it does not even compile. GCC 4-something for ARM says "error: conversion to non-scalar type requested"

How to make a va_list from a binary array in a portable way? Or at least for 32-bit and 64-bit archs separately - is possible without any "native call interface" libraries? If this is impossible, then is there any other standard or GNU library function suitable for this task?

Example of code that calls this:

#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#ifndef __GNUC__
#error Only Linux, other platforms/compilers not needed
#endif

int main() {
uint32_t data[10];
int i;
const char *fmt = "%x %x %x %x\n";
// Simulation of reading the data
for(i = 0; i < 10, i++) data[i] = 0x100 + i;
print_stuff(data, fmt);
return 0;
}

Solution

  • Transferring salient comments into an approximation to an answer.

    You will have to review and revise the calling code to do the job properly. Or you give up your simple call to vprintf() and work a lot harder.

    If you are passing an array to your printing function, you are not passing a va_list. There are all sorts of things that happen to work on some particular implementation that don't work on all implementations. What you're finding out is that your current system worked in its niche habitat, but now it needs to move out of its niche, it doesn't work any more. Welcome to the world of 'undefined behaviour'. Code that uses undefined behaviour isn't required to work; it isn't required to fail, either.

    Even with your revised question, there is no (portable) way to do it. Period. How many entries are in the array? What do the format strings look like? Is the number of items to be printed fixed? Basically, you may be reduced to:

    int n_fields = count_fields(format);
    switch (n_fields)
    {
    case 1: printf(format, p[0]); break;
    case 2: printf(format, p[0], p[1]); break;
    …
    }
    

    That's portable and reliable. If the number of items in the array is fixed (your code shows 4 entries used), you don't need to do the counting or the switch, of course.

    You might lookup libffi; it may be able to help you.