c - Pasando número variable de argumentos alrededor
variadic-functions (10)
Aunque puede resolver el paso del formateador almacenándolo en un 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.
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 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);
}
Digamos que tienes una función variad 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 usted?
Si envuelve su función variadic en una macro, no necesita 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 necesita especificar el argumento inicial cada vez.
En el magnífico C ++ 0x puedes usar varias plantillas:
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...);
}
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).
// 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);}
No estoy seguro 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 (). Así que 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 desea, puede pasar punteros a otras funciones y usarlas también. Puedes hacer tus propias macros fácilmente. Todo lo que hacen las macros 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.
No hay forma de llamar (por ejemplo) a 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
de va_list
en lugar de ...
Las ...
versiones son solo envoltorios alrededor de las versiones de va_list
.
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);
}
Puede utilizar 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);
}
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;
}
Las funciones variables pueden ser dangerous . 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});