Utilisation de __FILE__, __LINE__ et __FUNCTION__ en C ++

158

En supposant que votre compilateur C ++ les prend en charge, y a-t-il une raison particulière de ne pas les utiliser __FILE__, __LINE__et __FUNCTION__à des fins de journalisation et de débogage?

Je suis principalement préoccupé par le fait de fournir à l'utilisateur des données trompeuses (par exemple, le fait de signaler le numéro de ligne ou la fonction incorrect à la suite d'une optimisation) ou de réduire les performances.

En fait, je peux faire confiance __FILE__, __LINE__et __FUNCTION__à toujours faire la bonne chose?

Runcible
la source
LINE doit faire ce qu'il faut. Je l'ai beaucoup utilisé et ses cohortes, y compris PRETTY_FUNCTION . ... Mais ... eh bien, je suis juste en train de regarder le code où se trouve LINE . Probablement parce qu'il est dans un bloc catch pour la gestion des exceptions try / catch.
Krazy Glew
4
pertinent: référence gcc pour les macros prédéfinies
Alexander Malakhov

Réponses:

191

__FUNCTION__n'est pas standard, __func__existe en C99 / C ++ 11. Les autres ( __LINE__et __FILE__) sont très bien.

Il signalera toujours le bon fichier et la bonne ligne (et la fonction si vous choisissez d'utiliser __FUNCTION__/ __func__). L'optimisation est un non-facteur car il s'agit d'une expansion de macro au moment de la compilation; il n'affecter les performances en aucune façon.

Evan Teran
la source
3
__func__est une sorte de problème en C ++. C99 ne dit pas un mot sur les arguments par défaut et ainsi de suite, des cas où il n'est pas si évident de savoir comment __func__se comporter en C ++.
wilhelmtell
4
@thr: pendant que vous faites un bon point. J'étais assez clair qu'il __func__existe en c99, pas en c ++. Quoi qu'il en soit, je pense qu'une implémentation raisonnable de __func__en c ++ entraînerait simplement le nom mutilé. Puisque je ne suis pas un rédacteur de compilateur, ce n'est pas vraiment mon appel.
Evan Teran
Quels compilateurs ne supportent pas __FUNCTION__du tout? Quels compilateurs sauf gcc récent traitent cela comme une variable, pas comme une macro?
bassin
36
__func__est maintenant au standard C ++ 11.
VX
38

Dans de rares cas, il peut être utile de changer la ligne donnée par __LINE__autre chose. J'ai vu que GNU configure le fait pour certains tests pour signaler les numéros de ligne appropriés après avoir inséré du vaudou entre des lignes qui n'apparaissent pas dans les fichiers source d'origine. Par exemple:

#line 100

Les lignes suivantes commenceront par __LINE__100. Vous pouvez éventuellement ajouter un nouveau nom de fichier

#line 100 "file.c"

Ce n'est que rarement utile. Mais si c'est nécessaire, je ne connais aucune alternative. En fait, au lieu de la ligne, une macro peut également être utilisée, ce qui doit entraîner l'une des deux formes ci-dessus. À l'aide de la bibliothèque de préprocesseurs boost, vous pouvez incrémenter la ligne actuelle de 50:

#line BOOST_PP_ADD(__LINE__, 50)

J'ai pensé qu'il était utile de le mentionner puisque vous avez posé des questions sur l'utilisation de __LINE__et __FILE__. On n'a jamais assez de surprises en C ++ :)

Edit: @Jonathan Leffler fournit d'autres bons cas d'utilisation dans les commentaires:

La manipulation de #line est très utile pour les pré-processeurs qui souhaitent conserver les erreurs signalées dans le code C de l'utilisateur en ligne avec le fichier source de l'utilisateur. Yacc, Lex et (plus chez moi) les préprocesseurs ESQL / C le font.

Johannes Schaub - litb
la source
29

Pour info: g ++ propose la macro non standard __PRETTY_FUNCTION__. Jusqu'à présent, je ne connaissais pas C99 __func__ (merci Evan!). Je pense que je préfère toujours __PRETTY_FUNCTION__ lorsqu'il est disponible pour la portée de la classe supplémentaire.

PS:

static string  getScopedClassMethod( string thePrettyFunction )
{
  size_t index = thePrettyFunction . find( "(" );
  if ( index == string::npos )
    return thePrettyFunction;  /* Degenerate case */

  thePrettyFunction . erase( index );

  index = thePrettyFunction . rfind( " " );
  if ( index == string::npos )
    return thePrettyFunction;  /* Degenerate case */

  thePrettyFunction . erase( 0, index + 1 );

  return thePrettyFunction;   /* The scoped class name. */
}
Monsieur Ree
la source
2
Bon à savoir sur __PRETTY_FUNCTION__. Très utile!
Zheng Qu
8

Personnellement, je suis réticent à les utiliser pour autre chose que pour le débogage des messages. Je l'ai fait, mais j'essaie de ne pas montrer ce genre d'informations aux clients ou aux utilisateurs finaux. Mes clients ne sont pas des ingénieurs et ne maîtrisent parfois pas l'informatique. Je pourrais enregistrer ces informations sur la console, mais, comme je l'ai dit, à contrecœur, sauf pour les versions de débogage ou pour les outils internes. Je suppose que cela dépend de la clientèle que vous avez, cependant.

Craig S
la source
29
"Je pourrais enregistrer ces informations sur la console" - ou mieux encore: les enregistrer dans un fichier afin qu'en cas de problème, vous puissiez demander au client de vous les envoyer ...
Christoph
7

C ++ 20 std::source_location

C ++ a finalement ajouté une option non-macro, et elle dominera probablement à un moment donné dans le futur lorsque C ++ 20 se généralisera:

La documentation dit:

constexpr const char * nom_fonction () const noexcept;

6 Renvoie: Si cet objet représente une position dans le corps d'une fonction, renvoie un NTBS défini par l'implémentation qui doit correspondre au nom de la fonction. Sinon, renvoie une chaîne vide.

où NTBS signifie "Chaîne d'octets terminée par une valeur nulle".

Je vais essayer quand le support arrivera à GCC, GCC 9.1.0 avec g++-9 -std=c++2ane le supporte toujours pas.

https://en.cppreference.com/w/cpp/utility/source_location l' utilisation des revendications sera comme:

#include <iostream>
#include <string_view>
#include <source_location>

void log(std::string_view message,
         const std::source_location& location std::source_location::current()
) {
    std::cout << "info:"
              << location.file_name() << ":"
              << location.line() << ":"
              << location.function_name() << " "
              << message << '\n';
}

int main() {
    log("Hello world!");
}

Sortie possible:

info:main.cpp:16:main Hello world!

__PRETTY_FUNCTION__vs __FUNCTION__vs __func__vsstd::source_location::function_name

Réponse à: Quelle est la différence entre __PRETTY_FUNCTION__, __FUNCTION__, __func__?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
1
Il existe <experimental/source_location>actuellement dans gcc-9.
陈浩南
5

Je les utilise tout le temps. La seule chose qui m'inquiète est de donner l'adresse IP dans les fichiers journaux. Si vos noms de fonctions sont vraiment bons, vous pouvez rendre un secret commercial plus facile à découvrir. C'est un peu comme l'expédition avec des symboles de débogage, mais plus difficile à trouver. Dans 99,999% des cas, rien de mauvais n'en sortira.

JeffCharter
la source
1
Bon point à évoquer. Il est simple d'extraire ces informations à l'aide de l' stringsutilitaire pour extraire toutes les données de type chaîne de l'exécutable. Même les exécutables compressés peuvent être extraits. Soyez très attentif à ce que vous envoyez à un site client. Souvent, les concurrents sont capables de mettre la main sur vos exécutables, même s'ils ne sont pas censés le faire.
Marty
Il est possible avec constexpr qui utilise une table de recherche ou XOR au niveau du bit, etc. pour masquer les littéraux de chaîne de votre binaire. Il existe divers exemples là-bas si vous recherchez. Bien sûr, l'obscurcissement n'est que l'obscurcissement et non la sécurité, mais si vous ne voulez pas rendre vos fichiers et vos fonctions évidents dans le binaire, c'est une option.
idij il y a