2010-02-05 16 views
6

Esta es una pregunta extraña, pero ¿existe una forma estándar de manipular el contenido de un va_list antes de pasarlo a otra función? Por ejemplo, supongamos que tengo dos funciones, sum y vsum:¿Manera estándar de manipular argumentos variados?

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; 
} 

Si llamo sum como sum(4, 1, 2, 3, 4), espero que para obtener el resultado 10. Ahora supongamos que en lugar de llamar vsum directamente, sum llama a una función intermedia , vsum_stub el que hace lo siguiente:

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); 
} 

Ahora cuando llamo sum(4, 1, 2, 3, 4), debería volver el número 20, ya que todos los vsum_stub incrementos de los valores de la va_list por 2. Esto no compila, por supuesto, ya que no puede tomar la dirección del resultado de va_arg. ¿Hay otra forma de hacer esto? Estoy trabajando en C99.


Antecedentes:

Estoy trabajando en una biblioteca que hace alguna traducción puntero de modo que los datos pueden ser almacenados en el montón en un formato más eficiente. Los programas se compilan con una transformación personalizada que convierte las llamadas a funciones de biblioteca como printf en mis propias funciones de código auxiliar (por ejemplo, hc_printf). hc_printf necesita traducir cualquier argumento de puntero (cadenas destinadas a %s) antes de pasar los argumentos a la función real printf.

Editar: Aquí hay un ejemplo de código. Digamos que tenemos una cadena foo. foo se asigna dinámicamente con una versión modificada de malloc que devuelve un puntero falso. El compilador modifica el programa para que pueda manejar punteros falsos. Por lo que este funciona:

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

Quiero escribir una función fake_vprintf como esto (en pseudocódigo):

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); 
} 

El programa llamaría fake_vprintf igual que el original vprintf usando el puntero falso. fake_vprintf traduce el puntero falso a un puntero real que puede usar el real vprintf.

+0

No debería 'int * arg = & va_arg (ap, int);' en realidad ser 'int * arg = va_arg (ap, int *);'? – dirkgently

+0

@dirkgently, no, el parámetro real dentro de la lista de argumentos es un int. Mi intención en ese ejemplo era obtener un puntero a ese int dentro de la lista para poder cambiar su valor. –

+0

Supuse que el 'vsum_stub' toma una lista de' int * '. De todos modos, ¿cree que el código que publiqué es de alguna ayuda? – dirkgently

Respuesta

2

Aha, según tengo entendido, su problema es la creación de un nuevo argumento va_list para pasar a las funciones estándar vprintf. Lo que a su vez requerirá que modifiques cada miembro de la lista. Sin embargo, dado que no hay una operación de captación/edición/inserción de elemento inteligente para dicha lista, usted está atascado.

Realmente no veo ninguna manera de hacer esto. Por supuesto, puede crear un vprintf aplicar las transformaciones in situ, un argumento a la vez. Mi sugerencia será: Reimplementar todas las funciones de biblioteca estándar; de todos modos, está escribiendo envoltorios. Esto implica cierto trabajo, pero ya está haciendo una parte de él con hc_printf, etc., ¿por qué no recorre toda la distancia (y adivine qué ahorrar en una llamada a función).

+0

Reimplementar vprintf y amigos es probablemente la solución más correcta. Esperaba que hubiera alguna forma de modificar valores en va_list con va_arg, pero supongo que tal cosa no es posible. Probablemente tendré que decidir si es mejor volver a implementar vprintf y mis amigos o tener alguna solución específica de la plataforma para cada plataforma. –

3

Probablemente no pueda usar va_list de una manera independiente de la plataforma.Tendrá que ver cómo su entorno define una va_list en stdarg.h, y luego escribir sus propias herramientas para trabajar con ella.

Por ejemplo, si un va_list es solo un (char *), puede hacer todo tipo de cosas con él.

// add 1000 to the integer stored on the stack and advance va_list 
*(int *)va_list += 1000; 
va_list += sizeof(int); 

Usted está diciendo al compilador que desea que se considere va_list un puntero a un int (a través de la int * fundido), luego tomar el valor (*) y añadir 1,000 a ella (+ = 1.000) . Ahora avance el puntero va_list al siguiente argumento en la pila.

+0

Esto es lo que estoy usando actualmente, así que +1 para eso. Funciona para x86-32 en Linux. Sin embargo, eventualmente necesitaré portar este código a ARM y x86-64, y creo que ambas arquitecturas pasan argumentos en los registros, lo que puede hacer que este enfoque sea mucho más complicado. –

Cuestiones relacionadas