0

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;
}
ddbug
  • 1,392
  • 1
  • 11
  • 25
  • 4
    Why would you even do something that you clearly are not supposed to do? Who wrote this code originally? That person deserves some very harsh words! – Some programmer dude Jul 13 '17 at 11:05
  • Because, dear Some Dude, in real life there are such things as deadlines and so on. And I am asking exactly how to code this properly. – ddbug Jul 13 '17 at 11:08
  • 4
    Deadlines? I think you misspelled incompetence. It's fun to do things like this as a teenager when you think you're so clever and have everything figured out. Then you run into the first portability issue and stop being clever. – Art Jul 13 '17 at 11:18
  • This isn't even the right way to do it on any system - I'm amazed it's ever worked – Chris Turner Jul 13 '17 at 11:19
  • 1
    You can't do it. Design a proper interface that does the things you need to do instead of digging even deeper when you're already in a deep hole. – Art Jul 13 '17 at 11:19
  • I'm voting to close this question as off-topic because it's asking an impossible thing. –  Jul 13 '17 at 11:21
  • 2
    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. – Jonathan Leffler Jul 13 '17 at 11:22
  • 1
    Many years ago I also used such method with casting to `va_list`, and it worked on i386 and other platforms, but one day I encountered a platform with different va_list implementation. So my functions stopped to work. Conclusion: you should work conventionally! – RedSoft Jul 13 '17 at 11:46
  • It is a possible thing, but I agree this should be formulated differently. Given: binary array of parameters, how to convert it to a structure suitable for vsprintf in a simple way (without using known libraries),for a given architecture (armv7). Only engineers please, no academics :) - dd – ddbug Jul 13 '17 at 11:46
  • @JonathanLeffler: the calling code is trivial. It reads an array of int32_t from some (not relevant) source, then calls the function print_stuff() with some pre-defined format. I agree this code is bad, but dead simple and it served us well for quite a while (x86 is a very widespread architecture) - dd. – ddbug Jul 13 '17 at 11:52
  • Show the code that does the calling! 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. – Jonathan Leffler Jul 13 '17 at 12:00
  • If someone uses UB tricks in the serious software development should be sacked instantly. Your poor client..... Do it properly. It will take 15 minutes more probably. – 0___________ Jul 13 '17 at 12:02
  • 1
    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. – Jonathan Leffler Jul 13 '17 at 12:03
  • You might try to look up [`libffi`](https://sourceware.org/libffi/). It may be able to help you. – Jonathan Leffler Jul 13 '17 at 12:06
  • @JonathanLeffler I know about libffi and other similar libs, but hoped to find a simpler solution, even not universal. – ddbug Jul 13 '17 at 12:16
  • Good luck. I don't know about non-portable solutions because they're not relevant to my work. – Jonathan Leffler Jul 13 '17 at 12:17
  • @JonathanLeffler please make your comment a reply, I'll mark it as answer. Of course I know the number of parameters because I produce the data. – ddbug Jul 13 '17 at 12:19
  • Want to print an array? Write a loop. – n. m. could be an AI Jul 13 '17 at 13:02

2 Answers2

4

Since the format is pre-defined, you know exactly how many parameters to pass in. So pass to your function exactly the parameters you need instead of passing an array. Then you change your function to use ... as a parameter and get a va_list from that.

So your function would look like this:

void print_stuff(const char *format, ...)
{
    va_list args;

    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}

And you would call it like this:

const char *fmt = "%x %x %x %x\n";
...
print_stuff(fmt, data[0], data[1], data[2], data[3]);
dbush
  • 205,898
  • 23
  • 218
  • 273
  • This solution was given by Jonathan Leffler earlier in a comment. and I'll mark it as answer. Thanks! - dd – ddbug Jul 13 '17 at 12:42
3

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thank you, this solution will work for me. The number of parameters is variable (not shown for simplicity). It requires pushing the parameters on the stack, which is limited in my system - but for small number of parameters will work. - dd – ddbug Jul 13 '17 at 12:47