La macro __FILE__ affiche le chemin complet


164

La macro standard prédéfinie __FILE__disponible en C montre le chemin complet du fichier. Existe-t-il un moyen de raccourcir le chemin? Je veux dire au lieu de

/full/path/to/file.c

je vois

to/file.c

ou

file.c

18
Ce serait vraiment formidable de trouver une solution de préprocesseur uniquement. J'ai peur que les suggestions basées sur les opérations de chaîne s'exécutent au moment de l'exécution.
cdleonard

9
Puisque vous utilisez gcc, je pense que vous pouvez changer ce que __FILE__contient en changeant le nom de fichier que vous passez sur la ligne de commande. Alors au lieu de gcc /full/path/to/file.c, essayez cd /full/path/to; gcc file.c; cd -;. Bien sûr, il y a un peu plus que cela si vous comptez sur le répertoire actuel de gcc pour le chemin d'inclusion ou l'emplacement du fichier de sortie. Edit: la documentation gcc suggère que c'est le chemin complet, pas l'argument du nom du fichier d'entrée, mais ce n'est pas ce que je vois pour gcc 4.5.3 sur Cygwin. Donc vous pouvez aussi bien l'essayer sous Linux et voir.
Steve Jessop

4
GCC 4.5.1 (spécialement conçu pour arm-none-eabi) utilise le texte exact du nom du fichier sur sa ligne de commande. Dans mon cas, c'était la faute de l'EDI pour invoquer GCC avec tous les noms de fichiers pleinement qualifiés au lieu de placer le répertoire actuel dans un endroit raisonnable (emplacement du fichier projet, peut-être?) Ou configurable et d'utiliser des chemins relatifs à partir de là. Je soupçonne que beaucoup d'EDI font cela (en particulier sous Windows) pour une sorte d'inconfort lié à l'explication de l'emplacement du répertoire "actuel" pour une application GUI.
RBerteig

3
@SteveJessop - j'espère que vous lirez ce commentaire. J'ai une situation où je vois __FILE__imprimé ../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.cet je veux savoir où gcc a compilé le fichier de sorte que ce fichier soit relativement situé comme il est affiché.
Chan Kim

1
Cette question n'est pas une dupe de celle liée. D'une part, celle liée concerne C ++, et les réponses se plongent par conséquent dans la macro ésotérique C ++. Deuxièmement, il n'y a rien dans la question de l'OP qui exige une solution macro. Il signale seulement solennellement un problème et pose une question ouverte.
Prof. Falken

Réponses:


167

Essayer

#include <string.h>

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

Pour Windows, utilisez «\\» au lieu de «/».


13
/est un séparateur de chemin valide dans Windows.
Hans Passant

11
/est un séparateur de chemin valide dans les noms de fichiers transmis à CreateFile () et ainsi de suite. Cependant, cela ne signifie pas toujours que vous pouvez utiliser /n'importe où dans Windows, car il existe une tradition (héritée de CPM) d'utiliser /comme argument principal à l'invite de commande. Mais un outil de qualité ferait attention à diviser les noms de fichiers à la fois avec des barres obliques et des barres obliques inverses pour éviter tout problème aux personnes qui parviennent à les utiliser /.
RBerteig

5
@AmigableClarkKant, non, vous pouvez mélanger les deux séparateurs dans le même nom de fichier.
RBerteig

2
Si votre plate-forme le prend en charge, elle char* fileName = basename(__FILE__); est définitivement présente sous Linux et OS X, mais je ne sais pas pour Windows.
JeremyP

14
Cela pourrait être abrégé en strrchr("/" __FILE__, '/') + 1. En ajoutant «/» à __FILE__, strrchr est assuré de trouver quelque chose, et donc le conditionnel?: N'est plus nécessaire.
ɲeuroburɳ

54

Voici une astuce si vous utilisez cmake. De: http://public.kitware.com/pipermail/cmake/2013-January/053117.html

Je copie le conseil donc tout est sur cette page:

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

Si vous utilisez GNU make, je ne vois aucune raison pour laquelle vous ne pourriez pas étendre cela à vos propres makefiles. Par exemple, vous pourriez avoir une ligne comme celle-ci:

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

$(SOURCE_PREFIX)est le préfixe que vous souhaitez supprimer.

Ensuite, utilisez __FILENAME__à la place de __FILE__.


11
J'ai peur que cela ne fonctionne pas pour le FICHIER référencé dans le fichier d'en-tête.
Baiyan Huang

2
D'accord avec @BaiyanHuang mais pas sûr que le commentaire soit clair. __FILE__n'est pas un simple symbole de préprocesseur, il passe au fichier courant est souvent utilisé pour émettre le nom du fichier courant (en-tête ou module source). Cela __FILENAME__n'aurait que la source la plus externe
nhed

3
La solution de cette réponse n'est pas portable car elle utilise des échappements Bourne shell. Mieux vaut utiliser CMake pour l'implémenter de manière propre et portable. Voir la réponse de la macro define_file_basename_for_sources ici.
Colin D Bennett

3
La variante GNU Make de ceci est CFLAGS + = -D__FILENAME __ = \ "$ (notdir $ <) \"
ctuffli

4
@firegurafiku Sur les plateformes embarquées, la taille du code est souvent plus une contrainte que la vitesse. Si vous avez des instructions de débogage dans chaque fichier, cela peut rapidement augmenter la taille du fichier avec des chaînes de chemin complet. Je travaille avec une telle plate-forme pour le moment, et le référencement __FILE__partout est en train de souffler hors de l'espace de code.
mrtumnus

26

Je viens de penser à une excellente solution à cela qui fonctionne à la fois avec les fichiers source et en-tête, est très efficace et fonctionne au moment de la compilation sur toutes les plates-formes sans extensions spécifiques au compilateur. Cette solution préserve également la structure de répertoires relative de votre projet, afin que vous sachiez dans quel dossier se trouve le fichier, et uniquement par rapport à la racine de votre projet.

L'idée est d'obtenir la taille du répertoire source avec votre outil de construction et de l'ajouter simplement à la __FILE__macro, en supprimant entièrement le répertoire et en affichant uniquement le nom du fichier commençant à votre répertoire source.

L'exemple suivant est implémenté à l'aide de CMake, mais il n'y a aucune raison qu'il ne fonctionne avec aucun autre outil de construction, car l'astuce est très simple.

Dans le fichier CMakeLists.txt, définissez une macro qui a la longueur du chemin vers votre projet sur 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}")

Sur votre code source, définissez une __FILENAME__macro qui ajoute simplement la taille du chemin source à la __FILE__macro:

#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)

Ensuite, utilisez simplement cette nouvelle macro au lieu de la __FILE__macro. Cela fonctionne car le __FILE__chemin commencera toujours par le chemin de votre répertoire source CMake. En le supprimant de la __FILE__chaîne, le préprocesseur se chargera de spécifier le nom de fichier correct et tout sera relatif à la racine de votre projet CMake.

Si vous vous souciez des performances, c'est aussi efficace que d'utiliser __FILE__, car les deux __FILE__et SOURCE_PATH_SIZEsont des constantes de temps de compilation connues, de sorte qu'elles peuvent être optimisées par le compilateur.

Le seul endroit où cela échouerait est si vous l'utilisez sur des fichiers générés et qu'ils se trouvent dans un dossier de construction hors source. Ensuite, vous devrez probablement créer une autre macro en utilisant la CMAKE_BUILD_DIRvariable au lieu de CMAKE_SOURCE_DIR.


4
Je n'ai pas compris cela au début. J'ai codé des exemples et les ai exécutés contre gcc et clang et ils fonctionnent. J'expérimente également en ajoutant simplement diverses valeurs numériques littérales, et cela se comporte comme je m'y attendais. Puis il m'est finalement apparu. __FILE__est un pointeur vers un tableau d'octets. Donc, l'ajout d'un littéral numérique n'est qu'un ajout de pointeur. Very clever @RenatoUtsch
Daniel

Cela ne fonctionne que si un fichier source se trouve sous le répertoire de liste cmake. Si un fichier source est à l'extérieur, il se cassera, peut-être avec un accès en dehors d'une chaîne littérale. Soyez donc prudent avec cela.
Andry

Cela ne réduit en fait pas la taille du code. Je suppose que tout le chemin est toujours compilé dans le binaire, seul le pointeur est modifié.
Tarion

La __FILE__macro et les macros SOURCE_PATH_SIZE sont des constantes connues au moment de la compilation. Je m'attendrais à ce que les compilateurs d'optimisation modernes soient capables de détecter qu'une partie de la chaîne n'est pas utilisée et de la supprimer simplement du binaire. Quoi qu'il en soit, je ne pense pas que ces quelques octets feraient une différence significative dans la taille binaire, donc je ne m'en soucierais pas vraiment.
RenatoUtsch

@RenatoUtsch Le projet sur lequel je travaille a un changement qui spécifie simplement le nom du fichier, mais a l'inconvénient de donner aussi le nom du fichier C à l'en-tête. La modification a été apportée afin d'obtenir des versions reproductibles. Donc, avec gcc avec -O2, la chaîne serait-elle effectivement optimisée et la construction rendue reproductible?
Paul Stelian

19

Solution de temps de compilation purement ici. Il est basé sur le fait qu'un sizeof()littéral de chaîne renvoie sa longueur + 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__)

N'hésitez pas à étendre la cascade d'opérateurs conditionnels au nom de fichier sensible maximal dans le projet. La longueur du chemin n'a pas d'importance, tant que vous vérifiez suffisamment loin de la fin de la chaîne.

Je vais voir si je peux obtenir une macro similaire sans longueur codée en dur avec la récursivité des macros ...


3
Réponse cool. Vous devez utiliser au moins -O1pour l'utiliser pour être au moment de la compilation.
Digital Trauma

1
N'est-ce pas à l'envers? Vous voulez trouver la dernière occurrence de «/», ce qui signifie que vous devez commencer par la sizeof(s) > 2vérification. En outre, cela n'a pas fonctionné au moment de la compilation pour moi, à -Os. Les chaînes de chemin complet étaient présentes dans le binaire de sortie.
mrtumnus

15

Au moins pour gcc, la valeur de __FILE__est le chemin du fichier tel que spécifié sur la ligne de commande du compilateur . Si vous compilez file.ccomme ceci:

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

le __FILE__va s'étendre à "/full/path/to/file.c". Si vous faites plutôt ceci:

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

puis __FILE__s'étendra à juste "file.c".

Cela peut être pratique ou non.

La norme C n'exige pas ce comportement. Tout ce qu'il dit, __FILE__c'est qu'il se développe en "Le nom présumé du fichier source actuel (une chaîne de caractères littérale)".

Une alternative consiste à utiliser la #linedirective. Il remplace le numéro de ligne actuel et éventuellement le nom du fichier source. Si vous souhaitez remplacer le nom du fichier mais laissez le numéro de ligne seul, utilisez la __LINE__macro.

Par exemple, vous pouvez ajouter ceci près du haut de file.c:

#line __LINE__ "file.c"

Le seul problème avec ceci est qu'il attribue le numéro de ligne spécifié à la ligne suivante , et le premier argument #linedoit être une séquence de chiffres afin que vous ne puissiez pas faire quelque chose comme

#line (__LINE__-1) "file.c"  // This is invalid

S'assurer que le nom de fichier dans la #linedirective correspond au nom réel du fichier est laissé comme exercice.

Au moins pour gcc, cela affectera également le nom de fichier indiqué dans les messages de diagnostic.


1
Keith Thompson c'est une excellente solution, merci. Le seul problème est qu'il semble que cette macro réduit la __LINE__valeur de un. Donc __LINE__écrit en ligne xgest évalué x-1. Au moins avec gcc 5.4.
elklepo

1
@Klepak: Vous avez raison, et c'est un comportement standard. La directive "fait que l'implémentation se comporte comme si la séquence suivante de lignes source commence par une ligne source qui a un numéro de ligne tel que spécifié par la séquence de chiffres". Et ce doit être une séquence de chiffres , vous ne pouvez donc pas l'utiliser #line __LINE__-1, "file.c". Je mettrai à jour ma réponse.
Keith Thompson

1
Que serait-il pour d'autres compilateurs comme Clang, MSVC ou Intel C Compiler?
Franklin Yu

8

Un peu tard à la fête, mais pour GCC, jetez un œil à l' -ffile-prefix-map=old=newoption:

Lors de la compilation de fichiers résidant dans le répertoire ancien , enregistrez toute référence à ceux-ci dans le résultat de la compilation comme si les fichiers résidaient dans le répertoire nouveau à la place. Spécifier cette option équivaut à spécifier toutes les -f*-prefix-mapoptions individuelles . Cela peut être utilisé pour créer des constructions reproductibles indépendantes de l'emplacement. Voir aussi -fmacro-prefix-mapet -fdebug-prefix-map.

Donc, pour mes builds Jenkins-ffile-prefix-map=${WORKSPACE}/=/ , j'ajouterai , et un autre pour supprimer le préfixe d'installation du package de développement local.

NOTE Malheureusement, l' -ffile-prefix-mapoption n'est disponible qu'à partir de GCC 8, comme c'est le cas -fmacro-prefix-map, je pense , pour la __FILE__partie. Pour, disons, GCC 5, nous n'avons que -fdebug-prefix-mapce qui ne (semble pas) affecter __FILE__.


1
L'option -ffile-prefix-mapimplique en effet les deux -fdebug-prefix-mapet les -fmacro-prefix-mapoptions. Voir aussi les références sur reproducible-builds.org/docs/build-path Le bogue GCC qui suit -fmacro-prefix-mapet -ffile-prefix-mapest gcc.gnu.org/bugzilla/show_bug.cgi?id=70268
Lekensteyn

6
  • C ++ 11
  • msvc2015u3, gcc5.4, clang3.8.0

    template <typename T, size_t S>
    inline constexpr size_t get_file_name_offset(const T (& str)[S], size_t i = S - 1)
    {
        return (str[i] == '/' || str[i] == '\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
    }
    
    template <typename T>
    inline constexpr size_t get_file_name_offset(T (& str)[1])
    {
        return 0;
    }

    '

    int main()
    {
         printf("%s\n", &__FILE__[get_file_name_offset(__FILE__)]);
    }

Le code génère un décalage de temps de compilation lorsque:

  • gcc: au moins gcc6.1 + -O1
  • msvc: place le résultat dans la variable constexpr:

      constexpr auto file = &__FILE__[get_file_name_offset(__FILE__)];
      printf("%s\n", file);
  • clang: persiste en ne compilant pas l'évaluation du temps

Il existe une astuce pour forcer les 3 compilateurs à compiler l'évaluation du temps même dans la configuration de débogage avec l'optimisation désactivée:

    namespace utility {

        template <typename T, T v>
        struct const_expr_value
        {
            static constexpr const T value = v;
        };

    }

    #define UTILITY_CONST_EXPR_VALUE(exp) ::utility::const_expr_value<decltype(exp), exp>::value

    int main()
    {
         printf("%s\n", &__FILE__[UTILITY_CONST_EXPR_VALUE(get_file_name_offset(__FILE__))]);
    }

https://godbolt.org/z/u6s8j3


Mec, c'est beau et ça marche, merci! Aucune idée pourquoi cette réponse est si sous-estimée.
romain

5

Dans VC, lors de l'utilisation /FC, se __FILE__développe jusqu'au chemin complet, sans l' /FCoption __FILE__développe le nom du fichier. ref: ici


4

Il n'y a pas de moyen de temps de compilation pour ce faire. Évidemment, vous pouvez le faire au moment de l'exécution en utilisant le runtime C, comme certaines des autres réponses l'ont démontré, mais au moment de la compilation, lorsque le pré-processeur entre en action, vous n'avez pas de chance.


1
la strrchrréponse pourrait vraisemblablement être calculée au moment de la compilation, bien que bien sûr toujours pas par le préprocesseur. Je ne sais pas si gcc le fait réellement, je n'ai pas vérifié, mais je suis presque sûr qu'il calcule strlendes chaînes littérales au moment de la compilation.
Steve Jessop

@Steve - peut-être, mais c'est une grande dépendance au comportement spécifique du compilateur.
Sean

Je ne pense pas que ce soit une grande dépendance, car je doute fort que ce code soit critique pour les performances. Et si c'est le cas, sortez-le de la boucle. Dans les cas où c'est un gros problème, parce que vous avez absolument besoin d'un littéral de chaîne contenant uniquement le nom de base, vous pouvez peut-être calculer la bonne chaîne au moment de la construction en exécutant un script sur la source.
Steve Jessop

5
Cela peut ne pas être critique pour les performances, mais il peut facilement être considéré comme essentiel pour la confidentialité. Il n'y a aucune vraie bonne raison de révéler mes pratiques organisationnelles par client dans des chaînes figées dans un fichier EXE publié. Pire encore, pour les applications créées pour le compte d'un client, ces chaînes peuvent révéler des choses que mon client préférerait ne pas faire, comme ne pas être l'auteur de son propre produit. Comme __FILE__est invoquée implicitement par assert(), cette fuite peut se produire sans aucun autre acte manifeste.
RBerteig

@RBerteig le nom de base de __FILE__lui-même peut également révéler des choses que le client pourrait préférer ne pas faire, donc utiliser __FILE__n'importe où - qu'il contienne le chemin absolu complet ou juste le nom de base - présente les mêmes problèmes que vous avez signalés. Dans cette situation, toute la sortie devra être examinée et une API spéciale devrait être introduite pour la sortie aux clients. Le reste de la sortie doit être sauvegardé dans / dev / NULL ou stdout et stderr doit être fermé. :-)
tchen

4

Utilisez la fonction basename () ou, si vous êtes sous Windows, _splitpath () .

#include <libgen.h>

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

Essayez également man 3 basenamedans une coque.


2
@mahmood: char file_copy[] = __FILE__; const char *filename = basename(__FILE__);. La raison de la copie est que le nom de base peut modifier la chaîne d'entrée. Vous devez également faire attention que le pointeur de résultat n'est bon que jusqu'à ce qu'il basenamesoit appelé à nouveau. Cela signifie qu'il n'est pas thread-safe.
Steve Jessop

@SteveJessop, ah j'ai oublié. Vrai.
Prof.Falken

1
@Amigable: pour être honnête, je soupçonne qu'en basenamefait, ne modifiera pas la chaîne d'entrée qui en résulte __FILE__, car la chaîne d'entrée n'a pas de /à la fin et il n'y a donc pas besoin de modification. Vous pourriez donc vous en tirer, mais je pense que la première fois que quelqu'un le voit basename, il devrait le voir avec toutes les restrictions.
Steve Jessop

@SteveJessop la page de manuel BSd pour basename () mentionne que la version héritée de basename () prend un const char * et ne modifie pas la chaîne. La page de manuel Linux ne mentionne rien à propos de const mais mentionne qu'elle peut renvoyer une partie de la chaîne d'arguments. Donc, mieux vaut être prudent avec basename ().
Prof.Falken

@SteveJessop, hehe, ce n'est que maintenant qu'après avoir examiné attentivement votre commentaire, après quatre ans , je me rends compte qu'à /la fin de la chaîne signifie basename peut avoir une bonne raison de modifier son argument.
Prof. Falken

4

Si vous utilisez CMAKEavec le compilateur GNU, cette globaldéfinition fonctionne bien:

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

4

J'utilise la même solution avec la réponse de @Patrick depuis des années.

Il y a un petit problème lorsque le chemin complet contient un lien de symbole.

Meilleure solution.

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath $<))\"'")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath $<))\"'")

Pourquoi utiliser cela?

  • -Wno-builtin-macro-redefinedpour désactiver les avertissements du compilateur pour la redéfinition de la __FILE__macro.

    Pour que ces compilateurs ne prennent pas en charge cela, reportez-vous à la méthode Robust ci-dessous.

  • Supprimer le chemin du projet du chemin du fichier est votre véritable exigence. Vous n'aimerez pas perdre de temps à savoir où se trouve un header.hfichier, src/foo/header.hou src/bar/header.h.

  • Nous devrions supprimer la __FILE__macro dans le cmakefichier de configuration.

    Cette macro est utilisée dans la plupart des codes existants. Le redéfinir simplement peut vous libérer.

    Les compilateurs comme gccprédéfinissent cette macro à partir des arguments de ligne de commande. Et le chemin complet est écrit en makefiles généré par cmake.

  • Un code en dur CMAKE_*_FLAGSest requis.

    Il existe des commandes pour ajouter des options ou des définitions de compilateur dans une version plus récente, comme add_definitions()et add_compile_definitions(). Ces commandes analyseront les fonctions make comme substavant et s'appliqueront aux fichiers source. Ce n'est pas ce que nous voulons.

Un moyen robuste pour -Wno-builtin-macro-redefined.

include(CheckCCompilerFlag)
check_c_compiler_flag(-Wno-builtin-macro-redefined SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
if (SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined")
endif (SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-Wno-builtin-macro-redefined SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)
if (SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined")
endif (SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)

N'oubliez pas de supprimer cette option du compilateur de la set(*_FLAGS ... -D__FILE__=...)ligne.


Cela ne fonctionne pas pour le contenu provenant de fichiers d'inclusion.
chqrlie

pouvez-vous poster du code? Un cas courant consiste à définir des variables dans une portée locale et à les utiliser dans une autre.
Levi.G

Par exemple, si vous utilisez __FILE__avec votre définition pour produire un diagnostic dans une fonction en ligne définie dans un fichier d'en-tête, le diagnostic d'exécution signalera le nom du fichier passé au compilateur au lieu du nom du fichier d'inclusion, alors que le numéro de ligne reportez-vous au fichier d'inclusion.
chqrlie

oui, il a été conçu pour être cela, car l'usage le plus courant est #define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args). lorsque vous utilisez la LOG()macro, vous ne voulez pas vraiment voir log.hdans les messages. après tout, la __FILE__macro est développée dans chaque fichier C / Cpp (unité de compilation) au lieu des fichiers inclus.
Levi.G le

3

Une légère variation de ce que @ red1ynx proposait serait de créer la macro suivante:

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

Dans chacun de vos fichiers .c (pp), ajoutez:

SET_THIS_FILE_NAME();

Ensuite, vous pouvez vous référer à THIS_FILE_NAMEau lieu de __FILE__:

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

Cela signifie que la construction est effectuée une fois par fichier .c (pp) au lieu de chaque fois que la macro est référencée.

Il est limité à une utilisation uniquement à partir de fichiers .c (pp) et serait inutilisable à partir de fichiers d'en-tête.


3

J'ai fait une macro __FILENAME__qui évite de couper le chemin complet à chaque fois. Le problème est de conserver le nom de fichier résultant dans une variable cpp-locale.

Cela peut être facilement fait en définissant une variable globale statique dans le fichier .h . Cette définition donne des variables séparées et indépendantes dans chaque fichier .cpp qui inclut le .h . Afin d'être une preuve multithreading, il vaut la peine de rendre la (les) variable (s) également thread local (TLS).

Une variable stocke le nom de fichier (réduit). Un autre détient la valeur non coupée qui a __FILE__donné. Le fichier h:

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

La macro elle-même appelle la méthode avec toute la logique:

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

Et la fonction est implémentée de cette façon:

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

Le removePath () peut être implémenté de différentes manières, mais le rapide et simple semble être avec 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;
}


2

espérons simplement améliorer un peu la macro FILE:

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

ceci attrape / et \, comme Czarek Tomczak l'a demandé, et cela fonctionne très bien dans mon environnement mixte.


6
Définir une macro nommée FILEest une très mauvaise idée si vous incluez <stdio.h>.
Keith Thompson

bon à savoir. Je voulais juste montrer à Czarek ma solution \\ /, donc je ne me soucie pas des schémas de nommage.
alexander golks

2

Essayer

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

après les instructions d'inclusion dans votre fichier source et ajoutez

#pragma pop_macro("__FILE__")

à la fin de votre fichier source.


2
push_macroet ne pop_macrosont pas standard. (gcc les prend en charge pour la compatibilité avec les compilateurs Microsoft Windows.) Dans tous les cas, il est inutile de pousser et de faire apparaître la définition de __FILE__; la valeur restaurée ne sera de toute façon pas utilisée après la fin du fichier source. Une façon plus propre de changer la valeur de __FILE__est#line __LINE__ "foobar.c"
Keith Thompson

1
Et cela provoque une erreur interne dans le préprocesseur de gcc. gcc.gnu.org/bugzilla/show_bug.cgi?id=69665
Keith Thompson

1

Voici une fonction portable qui fonctionne à la fois pour Linux (chemin «/») et Windows (mélange de «\» et «/»).
Compile avec gcc, clang et 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;
}

Sortie standard:

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: 

1

Voici la solution qui utilise le calcul au moment de la 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__);

1

Si vous vous retrouvez sur cette page à la recherche d'un moyen de supprimer le chemin source absolu qui pointe vers l'emplacement de construction laide du binaire que vous expédiez, ci-dessous pourrait répondre à vos besoins.

Bien que cela ne produise pas exactement la réponse pour laquelle l'auteur a exprimé son souhait puisqu'il suppose l'utilisation de CMake , cela se rapproche assez. C'est dommage que cela n'ait été mentionné plus tôt par personne car cela m'aurait fait gagner beaucoup de temps.

OPTION(CMAKE_USE_RELATIVE_PATHS "If true, cmake will use relative paths" ON)

Définir la variable ci-dessus sur ONgénérera une commande de construction au format:

cd /ugly/absolute/path/to/project/build/src && 
    gcc <.. other flags ..> -c ../../src/path/to/source.c

En conséquence, la __FILE__macro se résoudra à../../src/path/to/source.c

Documentation CMake

Attention cependant à l'avertissement sur la page de documentation:

Utilisez des chemins relatifs (peut ne pas fonctionner!).

Il n'est pas garanti de fonctionner dans tous les cas, mais a fonctionné dans le mien - CMake 3.13 + gcc 4.5


La documentation de CMake 3.4+ indique que "Cette variable n'a aucun effet. L'effet partiellement implémenté qu'elle avait dans les versions précédentes a été supprimé dans CMake 3.4." Si cela a fonctionné pour vous avec 3.13, il y a une autre raison à cela.
mike.dld

0

Puisque vous utilisez GCC, vous pouvez profiter de

__BASE_FILE__Cette macro se développe au nom du fichier d'entrée principal, sous la forme d'une constante de chaîne C. Il s'agit du fichier source spécifié sur la ligne de commande du préprocesseur ou du compilateur C

puis contrôlez la manière dont vous souhaitez afficher le nom de fichier en modifiant la représentation du fichier source (chemin complet / chemin relatif / nom de base) au moment de la compilation.


6
ça ne fait aucune différence. J'ai utilisé __FILE__et __BASE_FILE__cependant ils montrent tous les deux le chemin complet du fichier
mahmood

comment invoquez-vous le compilateur?
ziu

2
Alors je parie que SCONS appelle gcc comme ça gcc /absolute/path/to/file.c. Si vous trouvez un moyen de changer ce comportement (en ouvrant une autre question sur SO, lol?), Vous n'avez pas besoin de modifier la chaîne au moment de l'exécution
ziu

17
Cette réponse est 100% fausse. __BASE_FILE__(comme le disent les documents, quoique peu clairs) produit le nom du fichier spécifié sur la ligne de commande , par exemple test.cou /tmp/test.cselon la façon dont vous avez appelé le compilateur. C'est exactement la même chose que __FILE__, sauf si vous êtes dans un fichier d'en-tête, auquel cas __FILE__produit le nom du fichier courant (par exemple foo.h) alors qu'il __BASE_FILE__continue à produire test.c.
Quuxplusone

0

Voici une solution qui fonctionne pour les environnements qui ne disposent pas de la bibliothèque de chaînes (noyau Linux, systèmes embarqués, 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; })

Maintenant, utilisez simplement à la FILENAMEplace de __FILENAME__. Oui, c'est toujours une chose d'exécution, mais cela fonctionne.


Peut vouloir vérifier à la fois «/» et «\», selon la plate-forme.
Technophile

0
#include <algorithm>
#include <string>
using namespace std;
string f( __FILE__ );
f = string( (find(f.rbegin(), f.rend(), '/')+1).base() + 1, f.end() );

// searches for the '/' from the back, transfers the reverse iterator 
// into a forward iterator and constructs a new sting with both

0

Une réponse courte et fonctionnelle pour Windows et * nix:

#define __FILENAME__ std::max<const char*>(__FILE__,\
    std::max(strrchr(__FILE__, '\\')+1, strrchr(__FILE__, '/')+1))

Pourquoi avez-vous besoin std::max<const char*>au lieu de juste std::max?
chqrlie
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.