Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Standard way to manipulate variadic arguments?

This is a weird question, but is there a standard way to manipulate the contents of a va_list before passing it to another function? For instance, suppose I have two functions, sum and vsum:

int vsum(int n, va_list ap) {
    int total = 0;
    for (int i = 0; i < n; ++i) {
        total += va_arg(n, int);
    return total;
}

int sum(int n, ...) {
    va_list ap;
    va_start(ap, n);
    int total = vsum(n, ap);
    va_end(ap);
    return total;
}

If I call sum as sum(4, 1, 2, 3, 4), I expect to get the result 10. Now let's suppose that instead of calling vsum directly, sum calls an intermediate function, vsum_stub which does the following:

int vsum_stub(int n, va_list ap) {
    va_list temp_ap;
    va_copy(temp_ap, ap);
    for (int i = 0; i < n; ++i) {
        int *arg = &va_arg(ap, int);
        *arg += 2;
    }
    va_end(temp_ap);
    return vsum(n, ap);
}

Now when I call sum(4, 1, 2, 3, 4), I should get back the result 20, since vsum_stub increments all of the values in the va_list by 2. This doesn't compile of course since you can't take the address of the result of va_arg. Is there another way to do this though? I'm working in C99.


Background:

I'm working on a library that does some pointer translation so that data may be stored on the heap in a more efficient format. Programs are compiled with a custom transformation which converts calls to library functions like printf to my own stub functions (e.g., hc_printf). hc_printf needs to translate any pointer arguments (strings intended for %s) before passing the arguments to the real printf function.

Edit: Here's a code example. Let's say we have a string foo. foo is dynamically allocated with a modified version of malloc which returns a fake pointer. The compiler modifies the program so that it can deal with fake pointers. So this works:

char *foo = fake_malloc(4);
fake_strcpy(foo, "foo");

I want to write a fake_vprintf function like this (in pseudocode):

int fake_vprintf(const char *format, va_list args) {
    for each pointer argument p in args
        translate p to q, a real pointer to contiguous memory
        replace p with q in args
    }
    return vprintf(format, args);
}

The program would call fake_vprintf just like the original vprintf using the fake pointer. fake_vprintf translates the fake pointer to a real pointer that the real vprintf can use.

like image 581
Jay Conrod Avatar asked Feb 05 '10 20:02

Jay Conrod


1 Answers

Aha, as I understand, your problem is creating a new va_list argument to pass on to the standard vprintf functions. Which in turn, will require you to modify each member of the list. However, since there is no element wise fetch/edit/insert operation for such a list you are stuck.

I don't really see any way of doing this. Of course, you can create a vprintf apply the transformations in situ, one argument at a time. My suggestion will be: Reimplement all such standard library functions -- at any rate you are writing wrappers. This involves some work, but you are already doing a part of it with hc_printf etc, so why not go the whole distance (and guess what save on a function call!).

like image 98
dirkgently Avatar answered Sep 18 '22 19:09

dirkgently