Comment “entrelacer” la source C / C ++ avec ma chaîne (uniquement à l’intérieur des fonctions aux endroits appropriés)?

Par exemple, il y a la source:

void func1() { func3(); if(qqq) { func2(); } func4( ); } 

Il devrait être transformé en:

 void func1() { MYMACRO func3(); MYMACRO if(qqq) { MYMACRO func2(); MYMACRO } MYMACRO func4( ); MYMACRO } 

C’est-à-dire d’insérer “MYMACRO \ n” à la fin de chaque ligne où l’instruction peut être, uniquement à l’intérieur des fonctions.

Comment le faire facilement? Devrais-je utiliser des expressions régulières? Quels outils devrais-je utiliser?

Par exemple, gcc peut-il afficher tous les numéros de ligne de toutes les instructions commençant (ou se terminant) dans des fonctions?

@related Comment dire à gcc d’instrumenter le code avec des appels à ma propre fonction chaque ligne du code?

@related Quel profileur dois-je utiliser pour mesurer le temps réel (y compris l’attente d’appels système) passé dans cette fonction, et non pas celui de _CPU_

Qu’essayez-vous d’accomplir en faisant cela? Sur la base de la description de la tâche, il existe probablement une manière beaucoup plus simple d’aborder le problème. Si vous êtes certain que c’est la meilleure façon d’accomplir votre tâche, lisez la suite.


Pour ce faire, vous devrez implémenter une sorte d’parsingur rudimentaire en langage C. Puisque vous traitez du texte, je vous recommande d’utiliser un langage de script tel que perl, python ou ruby ​​pour modifier votre texte au lieu d’écrire un programme C pour le faire.

Votre parsingur parcourra le fichier ligne par ligne et pour chaque ligne, il déterminera s’il doit insérer votre macro. L’parsingur devra garder trace d’un certain nombre de choses. Premièrement, il faut savoir si le commentaire est actuellement dans les commentaires. Lorsque vous rencontrez une séquence /* , définissez un indicateur “dans un commentaire” et effacez-le à la prochaine tentative de rencontre d’une séquence */ . Chaque fois que cet indicateur est défini, vous n’appendez pas d’invocation de macro. En outre, vous devrez savoir si vous êtes ou non dans une fonction. En supposant que votre code soit assez simple et direct, vous pouvez avoir un “compteur d’accolade” qui commence à zéro, s’incrémente chaque fois que vous rencontrez un { et décroît chaque fois que vous rencontrez un } . Si votre compteur d’accolade est égal à zéro, vous n’êtes pas à l’intérieur d’une fonction et vous ne devez pas append d’appel de macro. Vous voudrez également append un code spécial pour détecter et ignorer les accolades qui font partie d’une définition de structure, d’un initialiseur de tableau, etc. Notez que le simple comptage d’accolades ne fonctionnera pas si votre code fait des choses plus compliquées, telles que:

 void some_function (int arg) { #ifdef CHECK_LIMIT_ONLY if (arg == 0) { #else if (arg < 10) { #endif // some code here ... } } 

Bien que vous puissiez affirmer que l'extrait de code est simplement un exemple de code mal écrit, il ne s'agit que d'un exemple du type de problème que vous pouvez rencontrer. Si votre code contient quelque chose qui empêche le comptage simple d'accolades, alors ce problème est devenu nettement plus difficile. Une façon de savoir si votre code va rompre le comptage des accolades est si vous atteignez la fin du fichier avec un nombre d'accolades différent de zéro ou si, à un moment donné, le nombre d'accolades devient négatif.

Une fois que vous pouvez déterminer quand vous êtes dans une fonction et non dans un commentaire, vous devez déterminer si une macro doit être insérée après la ligne. Vous pouvez commencer avec quelques règles simples, tester le script et voir s'il y a des cas qu'il a manqués. Pour commencer, toute ligne se terminant par un point-virgule est la fin d'une instruction et vous voudrez insérer une macro après celle-ci. Semblable au comptage d'accolades, lorsque vous êtes à l'intérieur d'une fonction, vous souhaitez compter les parenthèses afin que vous puissiez déterminer si vous êtes à l'intérieur d'un appel de fonction, d'une boucle conditionnelle ou d'une autre instruction composée. Si vous êtes à l'intérieur de l'un de ceux-ci, vous n'appendez pas la macro. L’autre emplacement de code à suivre est le début et la fin d’un bloc { ... } . Si une ligne se termine par { ou } , vous appendez une macro après celle-ci.

Pour une tâche aussi compliquée que celle-ci, vous voudrez certainement créer un script, essayer un code relativement simple et voir ce qui ne va pas. Faites des ajustements pour couvrir les cas que vous avez manqués la première fois et re-testez. Quand il peut parsingr correctement le code simple, donnez-lui quelque chose de plus compliqué et voyez si bien.

'' Mise à jour: '' Pour répondre aux préoccupations exprimées par certaines personnes concernant la latence supplémentaire liée à l'ajout de commandes d'impression, n'oubliez pas que vous n'avez pas à imprimer d'horodatage à chaque appel de macro. Demandez à l'appel de macro de saisir un horodatage et de le coller dans une liste. Une fois votre programme terminé, imprimez tous les horodatages de la liste. Ainsi, vous enregistrez tous les délais liés à l’impression jusqu’à la fin du test.

réécrivez vos sources pour que les travaux suivants 🙂

Au lieu de gcc ... file1.c file2.c ... do

 gcc ... `sed -e's/;/;\nMYMACRO/' file1.c` file1extra.c \ `sed -e's/;/;\nMYMACRO/' file2.c` file2extra.c \ ... 

Voici un code C # rapide et sale. Fondamentalement juste des choses IO de fichier primitif Ce n’est pas génial, mais je l’ai préparé en 3 minutes environ. Ce code implique que les blocs de fonction sont délimités avec une ligne de commentaire “// FunctionStart” au début et “// FunctionEnd” à la fin. Il y a des manières plus élégantes de faire cela, c’est l’approche rapide / sale / hacky.

L’utilisation d’une application gérée pour effectuer cette tâche est probablement excessive, mais vous pouvez faire beaucoup de choses personnalisées en ajoutant simplement à cette fonction.

  private void InsertMacro(ssortingng filePath) { //Declrations: StreamReader sr = new StreamReader(filePath); StreamWriter sw = new StreamWriter(filePath + ".tmp"); ssortingng line = ""; bool validBlock = false; //Go through source file line by line: while ((line = sr.ReadLine()) != null) { if (line == "//FunctionStart") validBlock = true; else if (line == "//FunctionEnd") validBlock = false; sw.WriteLine(line); if (validBlock) sw.WriteLine("MYMACRO"); } //Replace legacy source with updated source: File.Delete(filePath); File.Move(filePath + ".tmp", filePath); //Clean up streams: sw.Close(); sr.Close(); }