¿Cómo escribir una macro que haga malloc, formatee una cadena y luego devuelva la cadena formateada?

He intentado esto:

#define format(f, ...) \
                int size = strlen(f) + (sizeof((int[]){__VA_ARGS__})/sizeof(int)) + 1); \
                char *buf = malloc(size); \
                snprintf(buf, size, f, __VA_ARGS__); \
                buf

Pero devuelve muchos errores sintácticos. ¿Cómo hago esto correctamente?

preguntado el 12 de junio de 12 a las 16:06

¿Puedo interponerme para decir que realmente no debería hacer esto por ningún otro motivo que no sea aprender cómo funciona el preprocesador C? -

.. ¿Y también anotar los errores exactos que estás recibiendo? -

3 Respuestas

Las macros de C no son funciones sino sustituciones 1:1. Así que si quieres usar tu macro así:

mystring = format("%d", 5);

Obtienes esto:

mystring = int size = strlen(f) + (sizeof((int[]){5})/sizeof(int)) + 1); \
            char *buf = malloc(size); \
            snprintf(buf, size, f, 5); \
            buf;

Lo cual no tiene ningún sentido. En su caso, es mejor que defina una función en línea que no debería ser peor en términos de rendimiento en un compilador decente.

Si democracia tiene que ser una macro y está en GCC, puede usar la declaración compuesta para lograr su objetivo. Te permite hacer esto: mystring = ({ statement1, statement2, ..., statementN}) que ejecutará todas sus declaraciones en un ámbito local y luego asignará statementN a mystring. Sin embargo, hará que su código no sea portátil y será un infierno para depurar.

Así que aquí tienes, pero por favor no uses esto en aplicaciones reales:

#define format(f, ...) \
    ({ int size = snprintf(NULL, 0, f, __VA_ARGS__) + 1;\
    char * buf = malloc(size);\
    snprintf(buf, size, f, __VA_ARGS__); buf; })

Lo digo en serio. No uses esto. Utilice una función en línea. También puede tener argumentos variádicos en funciones normales, usando va_arg y va_start:

inline char * format(f, ...) {
    va_list args;
    va_start(args, f);
    int size = vsnprintf(NULL, 0, f, args) + 1;
    char * buf = malloc(size);
    vsnprintf(buf, size, f, args);
    return buf;
}

Respondido el 13 de junio de 12 a las 08:06

No tengo idea de lo que estás tratando de hacer con la primera línea. Parece que está tratando de calcular la cantidad de argumentos, pero luego está pasando eso a malloc y utilizando el valor de retorno de malloc como un tamaño..?!

La forma correcta de asignar el espacio para snprintf es llamar primero snprintf con el puntero del búfer y el tamaño como cero, e inspeccione el valor de retorno. Agregar uno al valor de retorno (para terminación nula) le dará el tamaño que necesita asignar y pasar al segundo snprintf llamada.

Además, no intentes hacer esto con una macro; usar una función y vsnprintf.

Respondido el 12 de junio de 12 a las 16:06

Si usa esto sin un argumento, debe escribir

snprintf(buf, size, f, ## __VA_ARGS__);

en lugar de; de lo contrario, no truncará la coma final.

Además, si desea utilizar una cadena de formato, no puede estar seguro de que strlen(format) + length of every other elements es en realidad el tamaño que obtienes. Tienes que hacer una suposición y luego usar una función segura.

Devolver el valor tampoco es posible usando este tipo de sintaxis de macro. Sin embargo, es posible usar una extensión GNU. Vea la respuesta modificada a continuación.

Además, ¿qué quieres hacer con

sizeof((int[]){__VA_ARGS__}

? No es bueno en absoluto. No puede averiguar el tamaño de una lista de argumentos variados.

Intente algo como esto:

#define format(f, ...) \
            ({
            char *buf = malloc(4096); \
            snprintf(buf, 4096, f , ## __VA_ARGS__); \
            buf
        })

Espero que esto ayude.

Respondido el 12 de junio de 12 a las 16:06

El código de OP tiene problemas mucho mayores que este, y su código ni siquiera es válido C .. - R .. GitHub DEJA DE AYUDAR A ICE

está utilizando varias extensiones gcc, aquí. El OP no dijo nada sobre ese compilador específico: Jens Gustedt

Hm, escribí que solo es posible con extensiones GNU. De todos modos, ¿hay alguien que esté usando algo que no sea GCC para compilar C? Sí, hay LLVM-CLANG en OS X, pero admite extensiones GNU de todos modos. - usuario529758

@H2CO3 esa es una visión muy estrecha... también está Visual C y el Compilador Intel C, que se usan mucho. - Mensi

Eso es cierto, pero 1. Los compiladores de Intel son compatibles con la gran mayoría de las extensiones GNU, 2. Si se queja de que uso extensiones GNU, será mejor que no mencione Visual-C++, ya que no solo "extiende" el lenguaje sino que viola varios estándares para que la mayoría de las fuentes que se pueden compilar con GCC, Intel y varios otros compiladores (menores) (¡a veces incluso C89 válido!) no se puedan traducir usando VC++. Además, en la pregunta del usuario, la macro variádica en sí misma es una extensión GNU. - usuario529758

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.