__FILE__ macro mostra o caminho completo

O MACRO __FILE__ predefinido padrão disponível em C mostra o caminho completo para o arquivo. Existe alguma maneira de encurtar o caminho? Quero dizer, em vez de

/full/path/to/file.c 

Entendo

 to/file.c 

ou

 file.c 

    Experimentar

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

    Para o Windows, use ‘\\’ em vez de ‘/’.

    Aqui vai uma dica se você estiver usando o cmake. De: http://public.kitware.com/pipermail/cmake/2013-Janeiro/053117.html

    Estou copiando a dica, então está tudo nesta página:

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

    Se você está usando o GNU make, não vejo razão para você não poder estender isso para seus próprios makefiles. Por exemplo, você pode ter uma linha como esta:

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

    onde $(SOURCE_PREFIX) é o prefixo que você deseja remover.

    Em seguida, use __FILENAME__ no lugar de __FILE__ .

    Acabei de pensar em uma ótima solução para isso que funciona com arquivos de origem e de header, é muito eficiente e funciona em tempo de compilation em todas as plataformas sem extensões específicas do compilador. Essa solução também preserva a estrutura de diretórios relativa do seu projeto, para que você saiba em qual pasta o arquivo está e apenas em relação à raiz do seu projeto.

    A idéia é obter o tamanho do diretório de origem com sua ferramenta de compilation e apenas adicioná-lo à macro __FILE__ , removendo o diretório inteiramente e mostrando apenas o nome do arquivo que inicia no diretório de origem.

    O exemplo a seguir é implementado usando o CMake, mas não há motivo para que ele não funcione com outras ferramentas de construção, porque o truque é muito simples.

    No arquivo CMakeLists.txt, defina uma macro que tenha o comprimento do caminho para o seu projeto no 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}") 

    Em seu código-fonte, defina uma macro __FILENAME__ que apenas adicione o tamanho do caminho de origem à macro __FILE__ :

     #define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE) 

    Em seguida, basta usar essa nova macro em vez da macro __FILE__ . Isso funciona porque o caminho __FILE__ sempre será iniciado com o caminho para o diretório de origem do CMake. Ao removê-lo da string __FILE__ o pré-processador se encarregará de especificar o nome correto do arquivo e tudo será relativo à raiz do seu projeto CMake.

    Se você se preocupa com o desempenho, isso é tão eficiente quanto usar __FILE__ , porque tanto __FILE__ quanto __FILE__ são constantes de tempo de compilation conhecidas, para que possa ser otimizado pelo compilador.

    O único local onde isso falharia é se você estiver usando isso em arquivos gerados e eles estiverem em uma pasta de compilation fora da fonte. Então você provavelmente terá que criar outra macro usando a variável CMAKE_BUILD_DIR vez de CMAKE_SOURCE_DIR .

    Solução de tempo puramente de compilation aqui. É baseado no fato de que sizeof() de uma string literal retorna seu tamanho + 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__) 

    Sinta-se à vontade para estender a cascata do operador condicional até o nome de arquivo mais sensato do projeto. O tamanho do caminho não importa, contanto que você cheque longe o suficiente do final da string.

    Verificarei se consigo obter uma macro semelhante sem comprimento codificado com recursion de macros …

    Pelo menos para o gcc, o valor de __FILE__ é o caminho do arquivo, conforme especificado na linha de comando do compilador . Se você compilar file.c assim:

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

    o __FILE__ expandirá para "/full/path/to/file.c" . Se você fizer isso:

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

    então __FILE__ expandirá para apenas "file.c" .

    Isso pode ou não ser prático.

    O padrão C não requer esse comportamento. Tudo o que diz sobre __FILE__ é que ele se expande para “O nome presumido do arquivo de origem atual (um literal de string de caractere)”.

    Uma alternativa é usar a diretiva #line . Ele substitui o número da linha atual e, opcionalmente, o nome do arquivo de origem. Se você quiser replace o nome do arquivo, mas deixar o número da linha sozinho, use a macro __LINE__ .

    Por exemplo, você pode adicionar isso perto do topo do file.c :

     #line __LINE__ "file.c" 

    Garantir que o nome do arquivo na diretiva #line corresponda ao nome real do arquivo é deixado como um exercício.

    Pelo menos para o gcc, isso também afetará o nome do arquivo relatado nas mensagens de diagnóstico.

    Não há tempo de compilation para fazer isso. Obviamente, você pode fazê-lo em tempo de execução usando o tempo de execução C, como algumas das outras respostas demonstraram, mas em tempo de compilation, quando o pré-processador entra em ação, você está sem sorte.

    Como você está usando o GCC, você pode aproveitar

    __BASE_FILE__ Esta macro se expande para o nome do arquivo de input principal, na forma de uma constante de string C. Este é o arquivo de origem que foi especificado na linha de comando do pré-processador ou do compilador C

    e, em seguida, controle como você deseja exibir o nome do arquivo, alterando a representação do arquivo de origem (caminho completo / caminho relativo / nome da base) no momento da compilation.

    Use a function basename () ou, se você estiver no Windows, _splitpath () .

     #include  #define PRINTFILE() { char buf[] = __FILE__; printf("Filename: %s\n", basename(buf)); } 

    Também tente man 3 basename em um shell.

    Uma pequena variação no que @ red1ynx propôs seria criar a macro a seguir:

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

    Em cada um dos seus arquivos .c (pp), adicione:

     SET_THIS_FILE_NAME(); 

    Então você pode se referir a THIS_FILE_NAME vez de __FILE__ :

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

    Isso significa que a construção é executada uma vez por arquivo .c (pp) em vez de cada vez que a macro é referenciada.

    Ele é limitado para uso somente de arquivos .c (pp) e seria inutilizável a partir de arquivos de header.

    Eu fiz uma macro __FILENAME__ que evita cortar o caminho completo a cada vez. O problema é manter o nome do arquivo resultante em uma variável cpp-local.

    Isso pode ser feito facilmente definindo uma variável global estática no arquivo .h . Essa definição fornece variables ​​separadas e independentes em cada arquivo .cpp que inclui o .h . Para ser uma prova multithreading, vale a pena fazer com que a variável também seja thread local (TLS).

    Uma variável armazena o nome do arquivo (encolhido). Outro detém o valor não cortado que __FILE__ deu. O arquivo h:

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

    A própria macro chama o método com toda a lógica:

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

    E a function é implementada desta maneira:

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

    O removePath () pode ser implementado de diferentes maneiras, mas o mais rápido e simples parece ser com 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; } 

    Se você estiver usando o CMAKE com o compilador GNU, esta definição global funciona bem:

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

    só espero melhorar um pouco a macro FILE:

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

    Isso captura / e /, como Czarek Tomczak solicitou, e isso funciona muito bem no meu ambiente misto.

    Experimentar

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

    após as instruções de inclusão no seu arquivo de origem e adicionar

     #pragma pop_macro("__FILE__") 

    no final do seu arquivo de origem.

    no vs, quando com / FC, FILE é igual ao caminho completo, sem / FC FILE é igual ao nome do arquivo. ref aqui

    Aqui está uma solução que funciona para ambientes que não possuem a biblioteca de strings (kernel Linux, sistemas embarcados, 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; }) 

    Agora é só usar FILENAME vez de __FILENAME__ . Sim, ainda é uma coisa de tempo de execução, mas funciona.

    Aqui está uma function portátil que funciona tanto para Linux (path ‘/’) como para Windows (mistura de ‘\’ e ‘/’).
    Compila com gcc, clang e vs.

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

    Saída padrão:

     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: 

    Aqui está a solução que usa o cálculo em tempo de compilation:

     constexpr auto* getFileName(const char* const path) { const auto* startPosition = path; for (const auto* currentCharacter = path;*currentCharacter != '\0'; ++currentCharacter) { if (*currentCharacter == '\\' || *currentCharacter == '/') { startPosition = currentCharacter; } } if (startPosition != path) { ++startPosition; } return startPosition; } std::cout << getFileName(__FILE__);