c file macros path

__FILE__ macro muestra la ruta completa



macros path (16)

El MACRO __FILE__ estándar predefinido disponible en C muestra la ruta completa al archivo. ¿Hay alguna manera de acortar el camino? Quiero decir en lugar de

/full/path/to/file.c

Ya veo

to/file.c

o

file.c


Acabo de pensar en una gran solución que funcione tanto con los archivos de origen como de cabecera, sea muy eficiente y funcione en tiempo de compilación en todas las plataformas sin extensiones específicas del compilador. Esta solución también conserva la estructura de directorio relativa de su proyecto, para que sepa en qué carpeta se encuentra el archivo, y solo en relación con la raíz de su proyecto.

La idea es obtener el tamaño del directorio de origen con su herramienta de compilación y simplemente agregarlo a la macro __FILE__ , eliminando el directorio por completo y solo mostrando el nombre del archivo comenzando en su directorio de origen.

El siguiente ejemplo se implementa con CMake, pero no hay ninguna razón por la que no funcione con otras herramientas de compilación, porque el truco es muy simple.

En el archivo CMakeLists.txt, defina una macro que tenga la longitud de la ruta de su proyecto en CMake:

# The additional / is important to remove the last character from the path. # Note that it does not matter if the OS uses / or /, because we are only # saving the path size. string(LENGTH "${CMAKE_SOURCE_DIR}/" SOURCE_PATH_SIZE) add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}")

En su código fuente, defina una macro __FILENAME__ que solo agrega el tamaño de la ruta fuente a la macro __FILE__ :

#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)

A continuación, utilice esta nueva macro en lugar de la macro __FILE__ . Esto funciona porque la ruta __FILE__ siempre comenzará con la ruta al directorio de origen de CMake. Al eliminarlo de la cadena __FILE__ el preprocesador se ocupará de especificar el nombre correcto del archivo y todo será relativo a la raíz de su proyecto CMake.

Si le importa el rendimiento, esto es tan eficiente como usar __FILE__ , porque ambos __FILE__ y SOURCE_PATH_SIZE son constantes de tiempo de compilación conocidas, por lo que el compilador puede optimizarlo.

El único lugar donde esto fallaría es si está utilizando esto en los archivos generados y están en una carpeta de compilación fuera de la fuente. Entonces, probablemente tendrá que crear otra macro utilizando la variable CMAKE_BUILD_DIR lugar de CMAKE_SOURCE_DIR .


Al menos para gcc, el valor de __FILE__ es la ruta del archivo como se especifica en la línea de comandos del compilador . Si compila file.c esta manera:

gcc -c /full/path/to/file.c

el __FILE__ se expandirá a "/full/path/to/file.c" . Si en cambio haces esto:

cd /full/path/to gcc -c file.c

entonces __FILE__ se expandirá a solo "file.c" .

Esto puede o no ser práctico.

El estándar C no requiere este comportamiento. Todo lo que dice acerca de __FILE__ es que se expande a "El nombre presunto del archivo fuente actual (un literal de cadena de caracteres)".

Una alternativa es usar la directiva #line . Anula el número de línea actual y, opcionalmente, el nombre del archivo de origen. Si desea sobrescribir el nombre del archivo pero deje solo el número de línea, use la macro __LINE__ .

Por ejemplo, puede agregar esto cerca de la parte superior de file.c :

#line __LINE__ "file.c"

Asegurarse de que el nombre del archivo en la directiva #line coincida con el nombre real del archivo se deja como ejercicio.

Al menos para gcc, esto también afectará el nombre de archivo informado en los mensajes de diagnóstico.


Aquí hay un consejo si estás usando cmake. De: http://public.kitware.com/pipermail/cmake/2013-January/053117.html

Estoy copiando la propina, así que todo está en esta página:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__FILENAME__=''/"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))/"''")

Si estás usando GNU make, no veo ninguna razón por la que no puedas extender esto a tus propios makefiles. Por ejemplo, puede tener una línea como esta:

CXX_FLAGS+=-D__FILENAME__=''/"$(subst $(SOURCE_PREFIX)/,,$(abspath $<))/"''"

donde $(SOURCE_PREFIX) es el prefijo que desea eliminar.

Luego use __FILENAME__ en lugar de __FILE__ .


Aquí hay una función portátil que funciona tanto para Linux (ruta ''/'') como para Windows (mezcla de ''/' y ''/'').
Compila con gcc, clang y vs.

#include <string.h> #include <stdio.h> const char* GetFileName(const char *path) { const char *name = NULL, *tmp = NULL; if (path && *path) { name = strrchr(path, ''/''); tmp = strrchr(path, ''//'); if (tmp) { return name && name > tmp ? name + 1 : tmp + 1; } } return name ? name + 1 : path; } int main() { const char *name = NULL, *path = NULL; path = __FILE__; name = GetFileName(path); printf("path: %s, filename: %s/n", path, name); path ="/tmp/device.log"; name = GetFileName(path); printf("path: %s, filename: %s/n", path, name); path = "C://Downloads//crisis.avi"; name = GetFileName(path); printf("path: %s, filename: %s/n", path, name); path = "C://Downloads/nda.pdf"; name = GetFileName(path); printf("path: %s, filename: %s/n", path, name); path = "C:/Downloads//word.doc"; name = GetFileName(path); printf("path: %s, filename: %s/n", path, name); path = NULL; name = GetFileName(NULL); printf("path: %s, filename: %s/n", path, name); path = ""; name = GetFileName(""); printf("path: %s, filename: %s/n", path, name); return 0; }

Salida estándar:

path: test.c, filename: test.c path: /tmp/device.log, filename: device.log path: C:/Downloads/crisis.avi, filename: crisis.avi path: C:/Downloads/nda.pdf, filename: nda.pdf path: C:/Downloads/word.doc, filename: word.doc path: (null), filename: (null) path: , filename:


Aquí hay una solución que funciona para entornos que no tienen la biblioteca de cadenas (kernel de Linux, sistemas integrados, etc.):

#define FILENAME ({ / const char* filename_start = __FILE__; / const char* filename = filename_start; / while(*filename != ''/0'') / filename++; / while((filename != filename_start) && (*(filename - 1) != ''/'')) / filename--; / filename; })

Ahora solo use FILENAME lugar de __FILENAME__ . Sí, todavía es una cosa de tiempo de ejecución, pero funciona.


Como está utilizando GCC, puede aprovechar

__BASE_FILE__ Esta macro se expande al nombre del archivo de entrada principal, en la forma de una constante de cadena C. Este es el archivo fuente que se especificó en la línea de comandos del preprocesador o compilador de C

y luego controle cómo desea visualizar el nombre de archivo cambiando la representación del archivo fuente (ruta completa / ruta relativa / nombre base) en el momento de la compilación.


Hice una macro __FILENAME__ que evita cortar el camino completo cada vez. El problema es mantener el nombre del archivo resultante en una variable cpp-local.

Se puede hacer fácilmente definiendo una variable global estática en el archivo .h . Esta definición proporciona variables independientes y separadas en cada archivo .cpp que incluye .h . Para que sea una prueba multithreading vale la pena hacer que la (s) variable (s) también se localicen local (TLS).

Una variable almacena el nombre del archivo (reducido). Otro contiene el valor no cortado que __FILE__ dio. El archivo h:

static __declspec( thread ) const char* fileAndThreadLocal_strFilePath = NULL; static __declspec( thread ) const char* fileAndThreadLocal_strFileName = NULL;

La macro misma llama método con toda la lógica:

#define __FILENAME__ / GetSourceFileName(__FILE__, fileAndThreadLocal_strFilePath, fileAndThreadLocal_strFileName)

Y la función se implementa de esta manera:

const char* GetSourceFileName(const char* strFilePath, const char*& rstrFilePathHolder, const char*& rstrFileNameHolder) { if(strFilePath != rstrFilePathHolder) { // // This if works in 2 cases: // - when first time called in the cpp (ordinary case) or // - when the macro __FILENAME__ is used in both h and cpp files // and so the method is consequentially called // once with strFilePath == "UserPath/HeaderFileThatUsesMyMACRO.h" and // once with strFilePath == "UserPath/CPPFileThatUsesMyMACRO.cpp" // rstrFileNameHolder = removePath(strFilePath); rstrFilePathHolder = strFilePath; } return rstrFileNameHolder; }

El removePath () se puede implementar de diferentes maneras, pero lo rápido y simple parece ser con strrchr:

const char* removePath(const char* path) { const char* pDelimeter = strrchr (path, ''//'); if (pDelimeter) path = pDelimeter+1; pDelimeter = strrchr (path, ''/''); if (pDelimeter) path = pDelimeter+1; return path; }


No hay tiempo de compilación para hacer esto. Obviamente, puede hacerlo en tiempo de ejecución utilizando el tiempo de ejecución de C, como han demostrado algunas de las otras respuestas, pero en el momento de la compilación, cuando el pre-procesador entra en acción, no tiene suerte.


Si está utilizando CMAKE con el compilador GNU, esta definición global funciona bien:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MY_FILE__=''/"$(notdir $(abspath $<))/"''")


Solución de tiempo puramente de compilación aquí. Se basa en el hecho de que sizeof() de una cadena literal devuelve su longitud +1.

#define STRIPPATH(s)/ (sizeof(s) > 2 && (s)[sizeof(s)-2] == ''/'' ? (s) + sizeof(s) - 1 : / sizeof(s) > 3 && (s)[sizeof(s)-3] == ''/'' ? (s) + sizeof(s) - 2 : / sizeof(s) > 4 && (s)[sizeof(s)-4] == ''/'' ? (s) + sizeof(s) - 3 : / sizeof(s) > 5 && (s)[sizeof(s)-5] == ''/'' ? (s) + sizeof(s) - 4 : / sizeof(s) > 6 && (s)[sizeof(s)-6] == ''/'' ? (s) + sizeof(s) - 5 : / sizeof(s) > 7 && (s)[sizeof(s)-7] == ''/'' ? (s) + sizeof(s) - 6 : / sizeof(s) > 8 && (s)[sizeof(s)-8] == ''/'' ? (s) + sizeof(s) - 7 : / sizeof(s) > 9 && (s)[sizeof(s)-9] == ''/'' ? (s) + sizeof(s) - 8 : / sizeof(s) > 10 && (s)[sizeof(s)-10] == ''/'' ? (s) + sizeof(s) - 9 : / sizeof(s) > 11 && (s)[sizeof(s)-11] == ''/'' ? (s) + sizeof(s) - 10 : (s)) #define __JUSTFILE__ STRIPPATH(__FILE__)

Siéntase libre de extender la cascada de operador condicional al nombre de archivo sensible máximo en el proyecto. La longitud de la ruta no importa, siempre y cuando controle lo suficiente desde el final de la cadena.

Veré si puedo obtener una macro similar sin longitud codificada con macro recursión ...


Tratar

#include <string.h> #define __FILENAME__ (strrchr(__FILE__, ''/'') ? strrchr(__FILE__, ''/'') + 1 : __FILE__)

Para Windows, use ''//' en lugar de ''/''.


Tratar

#pragma push_macro("__FILE__") #define __FILE__ "foobar.c"

después de las declaraciones include en su archivo fuente y agregue

#pragma pop_macro("__FILE__")

al final de tu archivo fuente


Una ligera variación sobre lo que @ red1ynx propuso sería crear la siguiente macro:

#define SET_THIS_FILE_NAME() / static const char* const THIS_FILE_NAME = / strrchr(__FILE__, ''/'') ? strrchr(__FILE__, ''/'') + 1 : __FILE__;

En cada uno de sus archivos .c (pp), agregue:

SET_THIS_FILE_NAME();

Luego puede referirse a THIS_FILE_NAME lugar de a __FILE__ :

printf("%s/n", THIS_FILE_NAME);

Esto significa que la construcción se realiza una vez por archivo .c (pp) en lugar de cada vez que se hace referencia a la macro.

Está limitado a usar solo desde archivos .c (pp) y no se podría usar desde archivos de encabezado.


Utilice la función basename() o, si está en Windows, _splitpath() .

#include <libgen.h> #define PRINTFILE() { char buf[] = __FILE__; printf("Filename: %s/n", basename(buf)); }

También prueba man 3 basename en un shell.


en vs, cuando con / FC, FILE equivale a la ruta completa, sin / FC FILE equivale al nombre del archivo. ref here


solo espero mejorar un poco la macro de ARCHIVO:

#define FILE (strrchr(__FILE__, ''/'') ? strrchr(__FILE__, ''/'') + 1 : strrchr(__FILE__, ''//') ? strrchr(__FILE__, ''//') + 1 : __FILE__)

esto atrapa / y /, como solicitó Czarek Tomczak, y esto funciona muy bien en mi entorno mixto.