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
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.
/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.
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:
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:
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.
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.
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 ...
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.
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__.
template<typename T,size_t S>inlineconstexprsize_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>inlineconstexprsize_t get_file_name_offset(T (& str)[1]){return0;}
'
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:
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
{staticconstexprconst 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__))]);}
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.
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é. :-)
@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:
-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.
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:
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:
constchar*GetSourceFileName(constchar* strFilePath,constchar*& rstrFilePathHolder,constchar*& 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:
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"
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
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.
ç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.):
Peut vouloir vérifier à la fois «/» et «\», selon la plate-forme.
Technophile
0
#include<algorithm>#include<string>usingnamespace 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
__FILE__
contient en changeant le nom de fichier que vous passez sur la ligne de commande. Alors au lieu degcc /full/path/to/file.c
, essayezcd /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.__FILE__
imprimé../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.c
et je veux savoir où gcc a compilé le fichier de sorte que ce fichier soit relativement situé comme il est affiché.Réponses:
Essayer
Pour Windows, utilisez «\\» au lieu de «/».
la source
/
est un séparateur de chemin valide dans Windows./
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/
.char* fileName = basename(__FILE__);
est définitivement présente sous Linux et OS X, mais je ne sais pas pour Windows.strrchr("/" __FILE__, '/') + 1
. En ajoutant «/» à__FILE__
, strrchr est assuré de trouver quelque chose, et donc le conditionnel?: N'est plus nécessaire.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:
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:
où
$(SOURCE_PREFIX)
est le préfixe que vous souhaitez supprimer.Ensuite, utilisez
__FILENAME__
à la place de__FILE__
.la source
__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__FILE__
partout est en train de souffler hors de l'espace de code.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:
Sur votre code source, définissez une
__FILENAME__
macro qui ajoute simplement la taille du chemin source à la__FILE__
macro: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__
etSOURCE_PATH_SIZE
sont 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_DIR
variable au lieu deCMAKE_SOURCE_DIR
.la source
__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__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.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.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 ...
la source
-O1
pour l'utiliser pour être au moment de la compilation.sizeof(s) > 2
vé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.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 compilezfile.c
comme ceci:le
__FILE__
va s'étendre à"/full/path/to/file.c"
. Si vous faites plutôt ceci: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
#line
directive. 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
:Le seul problème avec ceci est qu'il attribue le numéro de ligne spécifié à la ligne suivante , et le premier argument
#line
doit être une séquence de chiffres afin que vous ne puissiez pas faire quelque chose commeS'assurer que le nom de fichier dans la
#line
directive 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.
la source
__LINE__
valeur de un. Donc__LINE__
écrit en lignex
gest évaluéx-1
. Au moins avec gcc 5.4.#line __LINE__-1, "file.c"
. Je mettrai à jour ma réponse.Un peu tard à la fête, mais pour GCC, jetez un œil à l'
-ffile-prefix-map=old=new
option: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-map
option 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-map
ce qui ne (semble pas) affecter__FILE__
.la source
-ffile-prefix-map
implique en effet les deux-fdebug-prefix-map
et les-fmacro-prefix-map
options. Voir aussi les références sur reproducible-builds.org/docs/build-path Le bogue GCC qui suit-fmacro-prefix-map
et-ffile-prefix-map
est gcc.gnu.org/bugzilla/show_bug.cgi?id=70268msvc2015u3, gcc5.4, clang3.8.0
'
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:clang
: persiste en ne compilant pas l'évaluation du tempsIl 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:
https://godbolt.org/z/u6s8j3
la source
Dans VC, lors de l'utilisation
/FC
, se__FILE__
développe jusqu'au chemin complet, sans l'/FC
option__FILE__
développe le nom du fichier. ref: icila source
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.
la source
strrchr
ré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 calculestrlen
des chaînes littérales au moment de la compilation.__FILE__
est invoquée implicitement parassert()
, cette fuite peut se produire sans aucun autre acte manifeste.__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é. :-)Utilisez la fonction basename () ou, si vous êtes sous Windows, _splitpath () .
Essayez également
man 3 basename
dans une coque.la source
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'ilbasename
soit appelé à nouveau. Cela signifie qu'il n'est pas thread-safe.basename
fait, 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 voitbasename
, il devrait le voir avec toutes les restrictions./
la fin de la chaîne signifie basename peut avoir une bonne raison de modifier son argument.Si vous utilisez
CMAKE
avec le compilateur GNU, cetteglobal
définition fonctionne bien:la source
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.
Pourquoi utiliser cela?
-Wno-builtin-macro-redefined
pour désactiver les avertissements du compilateur pour la redéfinition de la__FILE__
macro.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.h
fichier,src/foo/header.h
ousrc/bar/header.h
.Nous devrions supprimer la
__FILE__
macro dans lecmake
fichier de configuration.Cette macro est utilisée dans la plupart des codes existants. Le redéfinir simplement peut vous libérer.
Les compilateurs comme
gcc
prédéfinissent cette macro à partir des arguments de ligne de commande. Et le chemin complet est écrit enmakefile
s généré parcmake
.Un code en dur
CMAKE_*_FLAGS
est requis.Il existe des commandes pour ajouter des options ou des définitions de compilateur dans une version plus récente, comme
add_definitions()
etadd_compile_definitions()
. Ces commandes analyseront les fonctions make commesubst
avant et s'appliqueront aux fichiers source. Ce n'est pas ce que nous voulons.Un moyen robuste pour
-Wno-builtin-macro-redefined
.la source
__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.#define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args)
. lorsque vous utilisez laLOG()
macro, vous ne voulez pas vraiment voirlog.h
dans 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.Une légère variation de ce que @ red1ynx proposait serait de créer la macro suivante:
Dans chacun de vos fichiers .c (pp), ajoutez:
Ensuite, vous pouvez vous référer à
THIS_FILE_NAME
au lieu de__FILE__
: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.
la source
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:La macro elle-même appelle la méthode avec toute la logique:
Et la fonction est implémentée de cette façon:
Le removePath () peut être implémenté de différentes manières, mais le rapide et simple semble être avec strrchr:
la source
Le compilateur Clang récent a une
__FILE_NAME__
macro (voir ici ).la source
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.
la source
FILE
est une très mauvaise idée si vous incluez<stdio.h>
.Essayer
après les instructions d'inclusion dans votre fichier source et ajoutez
à la fin de votre fichier source.
la source
push_macro
et nepop_macro
sont 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"
Voici une fonction portable qui fonctionne à la fois pour Linux (chemin «/») et Windows (mélange de «\» et «/»).
Compile avec gcc, clang et vs.
Sortie standard:
la source
Voici la solution qui utilise le calcul au moment de la compilation:
la source
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.
Définir la variable ci-dessus sur
ON
générera une commande de construction au format: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:
Il n'est pas garanti de fonctionner dans tous les cas, mais a fonctionné dans le mien - CMake 3.13 + gcc 4.5
la source
Puisque vous utilisez GCC, vous pouvez profiter de
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.
la source
__FILE__
et__BASE_FILE__
cependant ils montrent tous les deux le chemin complet du fichiergcc /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__BASE_FILE__
(comme le disent les documents, quoique peu clairs) produit le nom du fichier spécifié sur la ligne de commande , par exempletest.c
ou/tmp/test.c
selon 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 exemplefoo.h
) alors qu'il__BASE_FILE__
continue à produiretest.c
.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.):
Maintenant, utilisez simplement à la
FILENAME
place de__FILENAME__
. Oui, c'est toujours une chose d'exécution, mais cela fonctionne.la source
la source
Une réponse courte et fonctionnelle pour Windows et * nix:
la source
std::max<const char*>
au lieu de justestd::max
?