Est-il possible, en utilisant le préprocesseur C / C ++, de compter les lignes d'un fichier source, dans une macro ou une sorte de valeur disponible au moment de la compilation? Par exemple, puis-je remplacer MAGIC1
, MAGIC2
et MAGIC3
dans ce qui suit, et obtenir la valeur 4 en quelque sorte lors de l'utilisation MAGIC3
?
MAGIC1 // can be placed wherever you like before the relevant
// lines - either right before them, or in global scope etc.
foo(); MAGIC2
bar(); MAGIC2
baz(); MAGIC2
quux(); MAGIC2
// ... possibly a bunch of code here; not guaranteed to be in same scope ...
MAGIC3
Remarques:
- Les extensions spécifiques au compilateur des capacités du préprocesseur sont acceptables mais indésirables.
- Si cela n'est possible qu'avec l'aide d'une partie du C ++, par opposition à la construction C, c'est également acceptable mais indésirable (c'est-à-dire que j'aimerais quelque chose qui fonctionnerait pour C).
- Évidemment, cela peut être fait en exécutant le fichier source via un script de processeur externe, mais ce n'est pas ce que je demande.
c++
c-preprocessor
einpoklum
la source
la source
__LINE__
qui représente le numéro de ligne actuel__COUNTER__
et / ouBOOST_PP_COUNTER
?int arr[MAGIC4]
et obtenir le nombre de lignes dans une section précédemment comptée de mon code.Réponses:
Il y a la
__LINE__
macro du préprocesseur qui vous donne un entier pour la ligne qui apparaît. Vous pouvez prendre sa valeur sur une ligne, puis sur une ligne ultérieure, et comparer.Si vous souhaitez compter les occurrences de quelque chose plutôt que des lignes source, il
__COUNTER__
peut s'agir d'une option non standard, prise en charge par certains compilateurs tels que GCC et MSVC.J'ai pris la valeur initiale de
__COUNTER__
car elle aurait pu être utilisée précédemment dans le fichier source ou dans certains en-têtes inclus.En C plutôt qu'en C ++, il y a des limitations sur les variables constantes, donc un
enum
pourrait être utilisé à la place.Remplacer la const par
enum
:la source
__COUNTER__
n'est pas standard en C ou C ++. Si vous savez que cela fonctionne avec des compilateurs particuliers, spécifiez-les.BEFORE
et ceAFTER
ne sont pas des macrosJe sais que la demande de l'OP est d'utiliser des macros, mais je voudrais ajouter une autre façon de faire qui n'implique pas l'utilisation de macros.
C ++ 20 présente la
source_location
classe qui représente certaines informations sur le code source, telles que les noms de fichier, les numéros de ligne et les noms de fonction. Nous pouvons l'utiliser assez facilement dans ce cas.Et l'exemple vivant ici .
la source
source_location
être expérimental en C ++ 20?source_location
fait désormais officiellement partie de C ++ 20. Vérifiez ici . Je n'ai tout simplement pas pu trouver la version du compilateur gcc sur godbolt.org qui le supporte déjà dans un sens non expérimental. Pouvez-vous expliquer un peu plus votre déclaration - je ne peux utiliser le nombre de lignes que dans la même portée que les lignes que j'ai comptées ?line_number_start
etline_number_end
dans cette portée, nulle part ailleurs. Si je le veux ailleurs, je dois le passer à l'exécution - ce qui va à l'encontre du but.line_number_end
visible au moment de la compilation en dehors de sa portée. Corrige moi si je me trompe.Pour être complet: si vous êtes prêt à ajouter
MAGIC2
après chaque ligne, vous pouvez utiliser__COUNTER__
:https://godbolt.org/z/i8fDLx (renvoie
3
)Vous pouvez le rendre réutilisable en stockant les valeurs de début et de fin de
__COUNTER__
.Dans l'ensemble, c'est cependant très lourd. Vous ne pourrez pas non plus compter les lignes qui contiennent des directives de préprocesseur ou qui se terminent par des
//
commentaires. J'utiliserais à la__LINE__
place, voir l'autre réponse.la source
static_assert
?__COUNTER__
est toujours nul au départ car d'autres en-têtes, etc. pourraient l'utiliser.__COUNTER__
deux fois et prendre la différence__COUNTER__
seul ne serait pas autorisé, et il doit être étendu à quelque chose ou cela ne comptera pas (je ne me souviens pas des règles à 100% à ce sujet).Une solution un peu plus robuste, permettant différents compteurs (tant qu'ils ne se mélangent pas, et il n'y a pas d'utilisation de
__COUNTER__
pour d'autres tâches):Cela masque les détails de l'implémentation (bien qu'il les cache à l'intérieur des macros ...). C'est une généralisation de la réponse de @ MaxLanghof. Notez que
__COUNTER__
peut avoir une valeur non nulle lorsque nous commençons un comptage.Voici comment il est utilisé:
En outre, il s'agit d'un C valide - si votre préprocesseur le prend en charge
__COUNTER__
, c'est-à-dire.Fonctionne sur GodBolt .
Si vous utilisez C ++, vous pouvez modifier cette solution pour ne même pas polluer l'espace de noms global - en plaçant les compteurs à l'intérieur
namespace macro_based_line_counts { ... }
, ounamespace detail
etc.)la source
Sur la base de votre commentaire, si vous souhaitez spécifier une taille de tableau (au moment de la compilation) en C ou C ++, vous pouvez faire
Si vous
sizeof(array)
en avez besoin dans les lignes intermédiaires, vous pouvez la remplacer par une référence de variable statique (sauf si elle doit absolument être une expression constante entière) et un compilateur d'optimisation devrait la traiter de la même manière (éliminer le besoin de placer la variable statique en mémoire)Une
__COUNTER__
solution basée sur le système (si cette extension est disponible) par opposition à une solution basée sur__LINE__
le même fonctionnement.constexpr
s en C ++ devrait fonctionner aussi bienenum
, maisenum
fonctionnera également en C simple (ma solution ci-dessus est une solution en C simple).la source
__COUNTER__
solution basée a aussi des problèmes: vous feriez mieux d'espérer que votre macro magique est le seul utilisateur de__COUNTER__
, au moins avant que vous n'ayez fini d'utiliser__COUNTER__
. Le problème se résume essentiellement aux faits simples qui__COUNTER__/__LINE__
sont des fonctionnalités du préprocesseur et le préprocesseur fonctionne en une seule passe, vous ne pouvez donc pas backpatch une expression constante entière plus tard sur la base de__COUNTER__
/__LINE__
. La seule façon (en C au moins) est d'éviter la nécessité en premier lieu, par exemple, en utilisant des déclarations de tableau vers l'avant sans taille (déclarations de tableau incomplètement typées).\
n'affecte pas__LINE__
- s'il y a un saut de ligne,__LINE__
augmente. Exemple 1 , exemple 2 .