Pasar un número variable de argumentos alrededor

7 minutos de lectura

avatar de usuario
Vicente Martí

Digamos 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 dentro, 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);
 }

  • Tu ejemplo me parece un poco extraño, ya que pasas fmt tanto a format_string() como a fprintf(). ¿Debería format_string() devolver una nueva cadena de alguna manera?

    –Kristopher Johnson

    15 de octubre de 2008 a las 17:14

  • El ejemplo no tiene sentido. Era solo para mostrar el esquema del código.

    – Vicente Martí

    15 de octubre de 2008 a las 17:22

  • “debería ser buscado en Google”: no estoy de acuerdo. Google tiene mucho ruido (información poco clara, a menudo confusa). ¡Tener una buena (respuesta votada y aceptada) en stackoverflow realmente ayuda!

    – Ansgar

    6 de febrero de 2009 a las 9:53

  • Solo para opinar: llegué a esta pregunta de Google, y debido a que se desbordaba la pila, estaba muy seguro de que la respuesta sería útil. ¡Así que pregunta!

    – tenpn

    24 de febrero de 2009 a las 12:18

  • @Ilya: si nadie nunca escribiera cosas fuera de Google, no habría información para buscar en Google.

    – Erik Kaplún

    29 de agosto de 2012 a las 20:27


avatar de usuario
SmacL

Para pasar los puntos suspensivos, inicializa un va_list como de costumbre y simplemente páselo a su segunda función. no usas va_arg(). 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);
}

  • El código se toma de la pregunta, y en realidad es solo una ilustración de cómo convertir elipses en lugar de algo funcional. si lo miras format_string difícilmente será útil tampoco, ya que tendría que hacer modificaciones in-situ para encontrarlo, lo que ciertamente tampoco debería hacerse. Las opciones incluirían deshacerse de format_string por completo y usar vfprintf, pero eso hace suposiciones sobre lo que format_string realmente hace, o hacer que format_string devuelva una cadena diferente. Editaré la respuesta para mostrar lo último.

    – SmacL

    24 de febrero de 2012 a las 16:35

  • Si su cadena de formato usa los mismos comandos de cadena de formato que printf, también puede obtener algunos compiladores como gcc y clang para que le avise si su cadena de formato no es compatible con los argumentos reales pasados. Consulte el atributo de función GCC ‘ formato’ para más detalles: gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html.

    –Doug Richardson

    16/11/2013 a las 19:00


  • Esto no parece funcionar si pasa los argumentos dos veces seguidas.

    – fotano

    10 de junio de 2015 a las 4:20


  • @fotanus: si llamas a una función con el argptr y la función llamada utiliza el argptr en absoluto, lo único seguro que puede hacer es llamar va_end() y luego reiniciar va_start(argptr, fmt); para reinicializar. O puedes usar va_copy() si su sistema lo admite (C99 y C11 lo requieren; C89/90 no).

    –Jonathan Leffler

    15 de junio de 2015 a las 1:32

  • Tenga en cuenta que el comentario de @ThomasPadron-McCarthy ahora está desactualizado y el fprintf final está bien.

    – Federico

    13/09/2016 a las 20:19

No hay forma de llamar (por ejemplo) printf sin saber cuántos argumentos le está 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 posee vprintf que toma un va_list en lugar de la .... Él ... Las versiones son solo envoltorios alrededor de la va_list versiones.

  • Tenga en cuenta que vsyslog es no POSIX obediente.

    – patryk.beza

    9 de agosto de 2016 a las 12:29

avatar de usuario
rosa perrone

Funciones Variádicas puede ser 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});

  • Aún mejor es este truco: #define callVardicMethodSafely(values...) ({ values *v = { values }; _actualFunction(values, sizeof(v) / sizeof(*v)); })

    – Ricardo J. Ross III

    3 de marzo de 2012 a las 22:54

  • @RichardJ.RossIII Me gustaría que ampliara su comentario, es difícilmente legible así, no puedo entender la idea detrás del código y en realidad parece muy interesante y útil.

    – Penélope

    30 de marzo de 2012 a las 10:09

  • @ArtOfWarfare no estoy seguro de estar de acuerdo en que es un mal truco, Rose tiene una gran solución pero implica escribir func( (escribir[]){val1, val2, 0}); que se siente torpe, mientras que si tuviera #define func_short_cut(…) func((type[]){VA_ARGS}); entonces simplemente podría llamar a func_short_cut(1, 2, 3, 4, 0); lo que le da la misma sintaxis que una función variádica normal con el beneficio adicional del ingenioso truco de Rose… ¿cuál es el problema aquí?

    – chrispepper1989

    24 de julio de 2013 a las 14:05


  • ¿Qué pasa si quieres pasar 0 como argumento?

    – Julian Gold

    10 de septiembre de 2014 a las 8:50

  • Esto requiere que sus usuarios recuerden llamar con una terminación 0. ¿Cómo es más seguro?

    – cp.engr

    26 de febrero de 2016 a las 21:26

avatar de usuario
usuario2023370

En magnífico C ++ 11, podría usar plantillas variadas:

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

  • No es portátil, depende del compilador e impide la optimización del compilador. Muy mala solución.

    – Geoffroy

    20/10/2011 a las 18:55

  • Al menos esto responde realmente a la pregunta, sin redefinirla.

    – kungfooman

    25 de noviembre de 2016 a las 5:43

Aunque puede resolver pasar el formateador almacenándolo primero en el búfer local, pero eso necesita apilarse y en algún momento puede ser un problema con el que lidiar. Intenté seguir 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.

  • No es portátil, depende del compilador e impide la optimización del compilador. Muy mala solución.

    – Geoffroy

    20/10/2011 a las 18:55

  • Al menos esto responde realmente a la pregunta, sin redefinirla.

    – kungfooman

    25 de noviembre de 2016 a las 5:43

Puedes probar macro también.

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad