Pregunta

Diga que tengo una función C que toma un número variable de argumentos: ¿Cómo puedo llamar a otra función que espera un número variable de argumentos desde adentro, pasando todos los argumentos que entraron en la primera función?

Ejemplo:

void format_string(char *fmt, ...);

void debug_print(int dbg_lvl, char *fmt, ...) {
    format_string(fmt, /* how do I pass all the arguments from '...'? */);
    fprintf(stdout, fmt);
 }
¿Fue útil?

Solución

Para pasar los puntos suspensivos, debe convertirlos en una lista_contraída y usar esa lista_contra en su segunda función. Específicamente;

void format_string(char *fmt,va_list argptr, char *formatted_string);


void debug_print(int dbg_lvl, char *fmt, ...) 
{    
 char formatted_string[MAX_FMT_SIZE];

 va_list argptr;
 va_start(argptr,fmt);
 format_string(fmt, argptr, formatted_string);
 va_end(argptr);
 fprintf(stdout, "%s",formatted_string);
}

Otros consejos

No hay forma de llamar a (p. ej.) printf sin saber cuántos argumentos le están pasando, a menos que quiera meterse en trucos traviesos y no portátiles.

La solución generalmente utilizada es proporcionar siempre una forma alternativa de funciones vararg, por lo que printf tiene vprintf que toma una va_list en lugar de el ... . Las versiones de ... son solo envoltorios alrededor de las versiones de va_list .

Variadic Functions can sé peligroso . Aquí hay un truco más seguro:

   void func(type* values) {
        while(*values) {
            x = *values++;
            /* do whatever with x */
        }
    }

func((type[]){val1,val2,val3,val4,0});

En el magnífico C ++ 0x puede usar plantillas variadic:

template <typename ... Ts>
void format_string(char *fmt, Ts ... ts) {}

template <typename ... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts ... ts)
{
  format_string(fmt, ts...);
}

Puede usar el ensamblaje en línea para la llamada de función. (en este código asumo que los argumentos son caracteres).

void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
    {
        va_list argumentsToPass;
        va_start(argumentsToPass, fmt);
        char *list = new char[numOfArgs];
        for(int n = 0; n < numOfArgs; n++)
            list[n] = va_arg(argumentsToPass, char);
        va_end(argumentsToPass);
        for(int n = numOfArgs - 1; n >= 0; n--)
        {
            char next;
            next = list[n];
            __asm push next;
        }
        __asm push fmt;
        __asm call format_string;
        fprintf(stdout, fmt);
    }

También puedes probar la macro.

#define NONE    0x00
#define DBG     0x1F
#define INFO    0x0F
#define ERR     0x07
#define EMR     0x03
#define CRIT    0x01

#define DEBUG_LEVEL ERR

#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...)  fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...)  if((DEBUG_LEVEL & X) == X) \
                                      DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)

int main()
{
    int x=10;
    DEBUG_PRINT(DBG, "i am x %d\n", x);
    return 0;
}

Aunque puede resolver el paso del formateador almacenándolo en el búfer local primero, pero eso necesita una pila y puede ser un problema para tratar. Intenté seguirlo y parece funcionar bien.

#include <stdarg.h>
#include <stdio.h>

void print(char const* fmt, ...)
{
    va_list arg;
    va_start(arg, fmt);
    vprintf(fmt, arg);
    va_end(arg);
}

void printFormatted(char const* fmt, va_list arg)
{
    vprintf(fmt, arg);
}

void showLog(int mdl, char const* type, ...)
{
    print("\nMDL: %d, TYPE: %s", mdl, type);

    va_list arg;
    va_start(arg, type);
    char const* fmt = va_arg(arg, char const*);
    printFormatted(fmt, arg);
    va_end(arg);
}

int main() 
{
    int x = 3, y = 6;
    showLog(1, "INF, ", "Value = %d, %d Looks Good! %s", x, y, "Infact Awesome!!");
    showLog(1, "ERR");
}

Espero que esto ayude.

La solución de Ross se limpió un poco. Solo funciona si todos los argumentos son punteros. Además, la implementación del lenguaje debe admitir la eliminación de una coma anterior si __VA_ARGS__ está vacío (tanto Visual Studio C ++ como GCC do).

// pass number of arguments version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}


// NULL terminated array version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}

Digamos que tienes una función variadic típica que has escrito. Debido a que se requiere al menos un argumento antes del variado ... , siempre debe escribir un argumento adicional en uso.

¿O no?

Si envuelve su función variadic en una macro, no necesita un argumento precedente. Considera este ejemplo:

#define LOGI(...)
    ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))

Obviamente, esto es mucho más conveniente, ya que no es necesario que especifique el argumento inicial todas las veces.

No estoy seguro de si esto funciona para todos los compiladores, pero ha funcionado hasta ahora para mí.

void inner_func(int &i)
{
  va_list vars;
  va_start(vars, i);
  int j = va_arg(vars);
  va_end(vars); // Generally useless, but should be included.
}

void func(int i, ...)
{
  inner_func(i);
}

Puede agregar ... a inner_func () si lo desea, pero no lo necesita. Funciona porque va_start usa la dirección de la variable dada como punto de inicio. En este caso, le estamos dando una referencia a una variable en func (). Entonces usa esa dirección y lee las variables después de eso en la pila. La función inner_func () está leyendo desde la dirección de pila de func (). Por lo tanto, solo funciona si ambas funciones usan el mismo segmento de pila.

Las macros va_start y va_arg generalmente funcionarán si les da cualquier var como punto de partida. Así que si lo deseas, puedes pasar punteros a otras funciones y usarlas también. Puedes hacer tus propias macros fácilmente. Todas las macros hacen es encasillar las direcciones de memoria. Sin embargo, hacer que funcionen para todos los compiladores y convenciones de llamada es molesto. Por lo tanto, generalmente es más fácil usar los que vienen con el compilador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top