separar rápido relleno orden nombres nombre izquierda funciones extraer derecha con compuestos como cambiar apellidos apellido c c-preprocessor

rápido - Extraer un nombre de función dentro de una macro



como separar nombres y apellidos en excel funciones izquierda derecha y relleno rápido (5)

En C, a menudo tenemos que ejecutar dicho código

if (! somefun(x, y, z)) { perror("somefun") }

¿Es posible crear una macro que, utilizada de la siguiente manera:

#define chkerr ... chkerr(somefun(x, y, z));

compilaría a lo anterior?

Ya sé que puedo usar la macro __VA_ARGS__ , pero esto requeriría que la llame como

chkerr(somefun, x, y, z)


C11 6.4.2.2 Identificadores predefinidos

El identificador __func__ será declarado implícitamente por el traductor como si, inmediatamente después de la llave de apertura de cada definición de función, la declaración

static const char __func__[] = "function-name";

apareció, donde nombre-función es el nombre de la función que encierra léxicamente.

Puedes usarlo de esta manera:

#define chkErr(exp) do { if (!(exp)) perror(__func__); } while (0) chkerr(somefun(x, y, z));

Desafortunadamente, esto generaría un mensaje de error con el nombre de la función de llamada, no con somefun . Aquí hay una variante simple que debería funcionar e incluso producir mensajes de error más informativos:

#define chkErr(exp) do { if (!(exp)) perror(#exp); } while (0) chkerr(somefun(x, y, z));

En caso de que somefun(x, y, z) devuelva un valor distinto de cero, el mensaje de error contendrá la cadena "somefun(x, y, z)" .

Puede combinar ambas técnicas para dar tanto la llamada ofensiva como la ubicación:

#include <errno.h> #include <stdio.h> #include <string.h> #define chkErr(exp) / do { if (!(exp)) / fprintf(stderr, "%s:%d: in function %s, %s failed: %s/n",/ __FILE__, __LINE__, __func__, #exp, strerror(errno)); / } while (0) chkerr(somefun(x, y, z));

Esto supone que somefun() devuelve 0 o NULL en caso de error y establece errno consecuencia. Sin embargo, tenga en cuenta que la mayoría de las llamadas del sistema devuelven un valor distinto de cero en caso de error.


No es posible extraer solo el nombre de la función. El procesador C ve los literales que pasas como tokens individuales, que no pueden ser manipulados. Sus únicas opciones son imprimir la función con argumentos como Aconcague sugiere o pasar el nombre como un parámetro separado:

#define chkErr(FUNCTION_NAME, FUNCTION_CALL) / if(!FUNCTION_CALL) / { / perror(#FUNCTION_NAME); / } chkErr(someFunction, someFunction(10, 12));


Puede utilizar el formato de llamada original:

chkerr(somefun(x, y, z));

Con una macro y una función auxiliar:

#define chkerr(fcall) / if (!fcall) { / perror(extract_fname(#fcall)); / } const char *extract_fname(const char *fcall);

La función extract_fname obtendría texto y devolvería todo hasta el paréntesis abierto.


Sí, es posible con una macro variad fea, insegura:

#define chkerr(func, ...) / if(!func(__VA_ARGS__)) / { / perror(#func); / } ... chkerr(somefunc, 1, 2, 3);

Pero es una muy mala idea.

Llamada a la cordura:

Si solo existiera el código original con el enunciado simple if , el lector pensaría "Aquí llaman a una función y hacen algún control de error básico. Bien, cosas básicas. Continuando ...". Pero después de los cambios, cualquiera que lea el código se congelará y pensará "WTF is this ???".

Nunca puede escribir una macro que sea más clara que la instrucción if, lo que hace que la instrucción if sea superior a la macro.

Algunas reglas a seguir:

  • Las macros similares a funciones son peligrosas e ilegibles. Sólo deben utilizarse como último recurso.
  • Evite inventar su propio lenguaje de macros secreto con macros similares a funciones. Los programadores de C que leen su código conocen a C. No conocen su lenguaje de macros secreto.
  • "Evitar escribir" es a menudo una mala razón para las decisiones de diseño del programa. Evitar la repetición de código es una buena justificación, pero llevarlo a los extremos afectará la legibilidad del código. Si evita la repetición de código y hace que el código sea más legible al mismo tiempo, es una buena cosa. Si lo hace pero el código se vuelve menos legible, es difícil de justificar.

Variante corta (ya viste):

#define chkErr(FUNCTION, ...) / if(!FUNCTION(__VA_ARGS__)) / { / perror(#FUNCTION); / }

Tenga en cuenta que esto puede imponer grandes problemas en las construcciones anidadas if / else o similares:

if(x) chkErr(f, 10, 12) //; //^ semicolon forgotten! else chkErr(f, 12, 10);

Se compilaría para codificar equivalente a lo siguiente:

if(x) { if(!f(10, 12)) perror("f"); else if(!f, 12, 10)) perror("f"); }

Obviamente, no es lo que se pretendía con el if / else escrito con las macros ... Así que realmente debería preferir que se vea como una función real (que requiere un punto y coma):

#define chkErr(FUNCTION, ...) / do / { / if(!FUNCTION(__VA_ARGS__)) / { / perror(#FUNCTION); / } / } / while(0)

Usted lo llamaría así:

chkErr(someFunction, 10, 12);

En caso de error, la salida sería:

someFunction: <error text>

Sin embargo , esto oculta el hecho de que realmente se llama a una función, lo que hace que sea más difícil de entender para los "forasteros". La misma salida, no oculta la llamada a la función, pero requiere una coma adicional entre la función y los argumentos (en comparación con una llamada a la función normal):

#define chkErr(FUNCTION, ARGUMENTS) / do / { / if(!FUNCTION ARGUMENTS) / { / perror(#FUNCTION); / } / } / while(0) chkErr(someFunction,(12, 10)); // ^ (!)

Otra variante con el encanto de retener la llamada de función imprimiría toda esta llamada de función:

#define chkErr(FUNCTION_CALL) / do / { / if(!FUNCTION_CALL) / { / perror(#FUNCTION_CALL); / } / } / while(0) chkErr(someFunction(10, 12));

En caso de error, la salida sería:

someFunction(10, 12): <error text>

Addendum: si realmente desea exactamente la salida como se muestra en la pregunta y aún tiene la función call retenida (sin comas en el medio), está un poco en problemas. En realidad, es posible, pero requiere un trabajo extra:

El problema es cómo funciona el preprocesador en argumentos de macro: cada argumento es un token. Puede combinar fichas fácilmente, pero no puede dividirlas.

Si se omiten las comas, la macro aceptará un solo token, al igual que en mi segunda variante. Claro, puedes hacerlo como lo hice yo, pero obtienes los argumentos de la función con. Este es un literal de cadena, y como el preprocesador no puede modificar los literales de cadena, tiene que operar en ellos en tiempo de ejecución .

El siguiente problema es, sin embargo, que los literales de cadena no son modificables. ¡Así que necesitas modificar una copia !

La siguiente variante haría todo este trabajo por ti:

#define chkErr(FUNCTION_CALL) / do / { / if(!FUNCTION_CALL) / { / char function_name[] = #FUNCTION_CALL; / char* function_name_end = strchr(function_name, ''(''); / if(function_name_end) / *function_name_end = 0; / perror(function_name); / } / } / while(0)

Bueno, decídete si vale la pena el esfuerzo ...

Por cierto, el espacio en blanco entre el nombre de la función y el paréntesis de apertura no se elimina. Si quieres ser perfecto :

unsigned char* end = (unsigned char*) function_name; while(*end && *end != ''('' && !isspace(*end)) ++end; *end = 0;

O, mucho mejor (gracias chqrlie por la pista):

function_name[strcspn(function_name, "( /t")] = 0;

Cualquier otra cosa que se me ocurra requeriría un paso de procesamiento previo adicional:

#define CAT(X, Y) CAT_(X, Y) #define CAT_(X, Y) X ## Y #define chkErr(FUNCTION_CALL) / do / { / if(!FUNCTION_CALL) / { / perror(CAT(CHK_ERR_TEXT_, __LINE__)); / } / } / while 0 chkErr(function(10, 12));

Ah, eh, esto daría lugar a un código como este:

if(!function(10, 12)) { perror(CHK_ERR_TEXT_42); }

Y ahora, ¿de dónde obtener estas macros? Bueno, el pre-procesamiento, ¿recuerdas? Posiblemente un script en perl o python, por ejemplo, generando un archivo de encabezado adicional tendría que incluir. Debería asegurarse de que este preprocesamiento se realice cada vez que se ejecute el preprocesador del compilador.

Bueno, no todo es imposible de resolver, pero dejaré esto a los masoquistas entre nosotros ...