Numéro de ligne C / C ++


110

Dans un souci de débogage, puis-je obtenir le numéro de ligne dans les compilateurs C / C ++? (méthode standard ou méthodes spécifiques pour certains compilateurs)

par exemple

if(!Logical)
    printf("Not logical value at line number %d \n",LineNumber);
    // How to get LineNumber without writing it by my hand?(dynamic compilation)

17
@Lucas: Certains d'entre nous préfèrent ne pas jouer avec les débogueurs. Ce genre de «déclaration d'assertion du pauvre» est parfois plus clair parce que c'est une partie permanente du code, et une documentation durable des choses qui devraient être vraies sur l'état du calcul.
S.Lott

13
@Lucas: Les débogueurs sont également moins qu'utiles pour les problèmes intermittents dans les programmes de longue durée, ou pour collecter des informations sur les problèmes des logiciels déployés sur les sites clients. Dans ces cas, la seule option est que le programme enregistre autant d'informations que possible sur l'état du programme, pour une analyse ultérieure.
KeithB

1
@Lucas Et les débogueurs ne fonctionnent pas aussi bien sur certains systèmes embarqués pour obtenir ces informations.
George Stocker

Réponses:


180

Vous devez utiliser la macro de préprocesseur __LINE__et __FILE__. Ce sont des macros prédéfinies et une partie de la norme C / C ++. Lors du prétraitement, ils sont remplacés respectivement par une chaîne constante contenant un entier représentant le numéro de ligne courant et par le nom du fichier courant.

Autres variables de préprocesseur:

  • __func__: nom de la fonction (cela fait partie de C99 , tous les compilateurs C ++ ne le prennent pas en charge)
  • __DATE__ : une chaîne de la forme "Mmm jj aaaa"
  • __TIME__ : une chaîne de forme "hh: mm: ss"

Votre code sera:

if(!Logical)
  printf("Not logical value at line number %d in file %s\n", __LINE__, __FILE__);

2
C99 utilise __func__ plutôt que __FUNCTION__, dont AFAIK est partiellement obsolète. La différence peut casser votre code, car __func__ ne peut pas être utilisé pour la concaténation de chaînes constantes de C.
Joseph Quinsey

1
Référence du manuel GCC: "__FUNCTION__ et __PRETTY_FUNCTION__ ont été traités comme des chaînes littérales; elles peuvent être utilisées pour initialiser des tableaux de caractères et elles peuvent être concaténées avec d'autres chaînes littérales. GCC 3.4 et les versions ultérieures les traitent comme des variables, comme __func__. En C ++, __FUNCTION__ et __PRETTY_FUNCTION__ ont toujours été des variables. "
Joseph Quinsey

Existe-t-il un moyen d'obtenir le numéro de ligne sous forme de chaîne, identique au nom de fichier? Je voudrais que le préprocesseur me donne par exemple la chaîne littérale "22" au lieu de l'entier 22.
sep332

1
@ sep332 Oui, mais le cpp est une bête étrange, il faut donc le faire en deux étapes avec des macro-arguments. #define S1(N) #N #define S2(N) S1(N) #define LINESTR S2(__LINE__). Voir c-faq.com/ansi/stringize.html
Rasmus Kaj

1
Strictement dit, ce __func__n'est pas une macro, c'est une variable implicitement déclarée.
HolyBlackCat

64

Dans le cadre de la norme C ++, il existe des macros prédéfinies que vous pouvez utiliser. La section 16.8 du standard C ++ définit entre autres la __LINE__macro.

__LINE__: Le numéro de ligne de la ligne source actuelle (une constante décimale).
__FILE__: Le nom présumé du fichier source (une chaîne de caractères littérale).
__DATE__: La date de traduction du fichier source (une chaîne de caractères littérale ...)
__TIME__: L'heure de traduction du fichier source (une chaîne de caractères littérale ...)
__STDC__: Si __STDC__est prédéfinie
__cplusplus: Le nom __cplusplusest défini à la valeur 199711L lorsque compilation d'une unité de traduction C ++

Donc, votre code serait:

if(!Logical)
  printf("Not logical value at line number %d \n",__LINE__);

19

Vous pouvez utiliser une macro avec le même comportement que printf () , sauf qu'elle inclut également des informations de débogage telles que le nom de la fonction, la classe et le numéro de ligne:

#include <cstdio>  //needed for printf
#define print(a, args...) printf("%s(%s:%d) " a,  __func__,__FILE__, __LINE__, ##args)
#define println(a, args...) print(a "\n", ##args)

Ces macros doivent se comporter de la même manière que printf () , tout en incluant des informations de type java stacktrace. Voici un exemple principal:

void exampleMethod() {
    println("printf() syntax: string = %s, int = %d", "foobar", 42);
}

int main(int argc, char** argv) {
    print("Before exampleMethod()...\n");
    exampleMethod();
    println("Success!");
}

Ce qui entraîne la sortie suivante:

main (main.cpp: 11) Avant exampleMethod () ...
exampleMethod (main.cpp: 7) syntaxe printf (): string = foobar, int = 42
main (main.cpp: 13) Succès!


pour le développement c, vous #include<stdio.h>
changeriez

11

Utilisez __LINE__(c'est un double trait de soulignement LINE double-underscore), le préprocesseur le remplacera par le numéro de ligne sur lequel il est rencontré.



5

C ++ 20 offre une nouvelle façon d'y parvenir en utilisant std :: source_location . Ceci est actuellement accessible dans gcc an clang comme std::experimental::source_locationavec #include <experimental/source_location>.

Le problème avec des macros comme __LINE__c'est que si vous voulez créer par exemple une fonction de journalisation qui affiche le numéro de ligne actuel avec un message, vous devez toujours passer en __LINE__tant qu'argument de fonction, car il est développé sur le site d'appel. Quelque chose comme ça:

void log(const std::string msg) {
    std::cout << __LINE__ << " " << msg << std::endl;
}

Affiche toujours la ligne de la déclaration de fonction et non la ligne d'où a logété réellement appelée. D'un autre côté, avec std::source_locationvous pouvez écrire quelque chose comme ceci:

#include <experimental/source_location>
using std::experimental::source_location;

void log(const std::string msg, const source_location loc = source_location::current())
{
    std::cout << loc.line() << " " << msg << std::endl;
}

Ici, locest initialisé avec le numéro de ligne pointant vers l'emplacement où a logété appelé. Vous pouvez l'essayer en ligne ici.


4

Essayez __FILE__et __LINE__.
Vous pourriez également trouver __DATE__et __TIME__utile.
Cependant, à moins que vous n'ayez à déboguer un programme côté client et que vous ayez donc besoin de consigner ces informations, vous devriez utiliser le débogage normal.


Pourquoi ai-je voté contre cela et pourquoi mmyers a-t-il modifié mon message?
Sanctus2099

@ Sanctus2099: Il a été modifié, car Markdown a transformé vos doubles traits de soulignement pour afficher FILE et LINE en caractères gras (ne vérifiez-vous pas à quoi ressemble votre réponse?). Un autre point pourrait être (du moins cela me semble ainsi maintenant) que vous avez donné une réponse 1 heure après qu'une réponse déjà correcte a été donnée, donc vous n'avez ajouté aucune valeur.
Felix Kling

Le double soulignement est la syntaxe de balisage pour le gras . Afin d'afficher correctement les doubles traits de soulignement, vous devez les échapper (comme ceci: \ _ \ _) ou utiliser des backticks pour les marquer comme raw code(comme ceci: `__`). @mmyers a tenté d'aider, mais il n'a échappé qu'à l'un des traits de soulignement, et il vous restait donc la syntaxe de balisage pour l' italique . Les votes négatifs sont un peu durs ici, cependant, je suis d'accord.
Matt B.

D'accord, je n'avais pas réalisé le fait que les doubles traits de soulignement transformaient le texte en gras et je devais y aller et je n'ai pas eu le temps de regarder à quoi ressemblait ma réponse. Je comprends maintenant. Même si ma réponse avait une heure de retard, c'était quand même une bonne réponse. Cela n'a ajouté aucune valeur, mais ce n'était pas mal non plus, donc aucune raison de voter contre. C'est ce que vous obtenez pour essayer d'aider ...
Sanctus2099

2
@ Sanctus2099 Certaines personnes refusent rapidement de voter, c'est pourquoi il est important de s'assurer que votre réponse est correcte. Dans ce cas, vous avez publié une mauvaise réponse et l'avez laissée sans modification pendant 4 heures. Vous n'avez personne d'autre à blâmer que vous-même.
meagar

2

Pour ceux qui pourraient en avoir besoin, une macro "FILE_LINE" pour imprimer facilement fichier et ligne:

#define STRINGIZING(x) #x
#define STR(x) STRINGIZING(x)
#define FILE_LINE __FILE__ ":" STR(__LINE__)

1

Étant donné que je suis également confronté à ce problème maintenant et que je ne peux pas ajouter de réponse à une question différente mais également valide posée ici , je vais fournir un exemple de solution pour le problème de: obtenir uniquement le numéro de ligne où la fonction a été appelée C ++ à l'aide de modèles.

Contexte: en C ++, on peut utiliser des valeurs entières non de type comme argument de modèle. Ceci est différent de l'utilisation typique des types de données comme arguments de modèle. L'idée est donc d'utiliser de telles valeurs entières pour un appel de fonction.

#include <iostream>

class Test{
    public:
        template<unsigned int L>
        int test(){
            std::cout << "the function has been called at line number: " << L << std::endl;
            return 0;
        }
        int test(){ return this->test<0>(); }
};

int main(int argc, char **argv){
    Test t;
    t.test();
    t.test<__LINE__>();
    return 0;
}

Production:

la fonction a été appelée au numéro de ligne: 0

la fonction a été appelée au numéro de ligne: 16

Une chose à mentionner ici est que dans C ++ 11 Standard, il est possible de donner des valeurs de modèle par défaut pour les fonctions utilisant un modèle. Dans la version antérieure à C ++ 11, les valeurs par défaut des arguments non de type semblent fonctionner uniquement pour les arguments de modèle de classe. Ainsi, dans C ++ 11, il n'y aurait pas besoin d'avoir des définitions de fonction en double comme ci-dessus. En C ++ 11, il est également valide d'avoir des arguments de modèle const char *, mais il n'est pas possible de les utiliser avec des littéraux comme __FILE__ou __func__comme mentionné ici .

Donc, à la fin, si vous utilisez C ++ ou C ++ 11, cela pourrait être une alternative très intéressante que d'utiliser des macros pour obtenir la ligne d'appel.


1

Utilisation __LINE__, mais quel est son type?

LINE Le numéro de ligne présumé (dans le fichier source actuel) de la ligne source actuelle (une constante entière).

En tant que constante entière , le code peut souvent supposer que la valeur est __LINE__ <= INT_MAXet donc le type est int.

Pour imprimer en C, a printf()besoin du prescripteur correspondant: "%d". C'est une préoccupation bien moindre en C ++ avec cout.

Inquiétude pédante: si le numéro de ligne dépasse INT_MAX1 (quelque peu concevable avec 16 bits int), espérons que le compilateur produira un avertissement. Exemple:

format '%d' expects argument of type 'int', but argument 2 has type 'long int' [-Wformat=]

Sinon, le code pourrait forcer des types plus larges à prévenir de tels avertissements.

printf("Not logical value at line number %ld\n", (long) __LINE__);
//or
#include <stdint.h>
printf("Not logical value at line number %jd\n", INTMAX_C(__LINE__));

Éviter printf()

Pour éviter toutes les limitations d'entiers: stringify . Le code pourrait s'imprimer directement sans printf()appel: une bonne chose à éviter dans la gestion des erreurs 2 .

#define xstr(a) str(a)
#define str(a) #a

fprintf(stderr, "Not logical value at line number %s\n", xstr(__LINE__));
fputs("Not logical value at line number " xstr(__LINE__) "\n", stderr);

1 Pratique de programmation certainement médiocre pour avoir un fichier aussi volumineux, mais peut-être que le code généré par la machine peut devenir élevé.

2 Lors du débogage, il arrive que le code ne fonctionne tout simplement pas comme prévu. L'appel de fonctions complexes comme *printf()peut entraîner des problèmes par rapport à un simple fputs().

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.