Auf der Suche nach einem Präprozessor-Trick...



  • Hi,

    ich habe ein kleines, kniffliges Problem. Ich habe mir einen kleinen Function-Guard gebastelt damit ich Stack-Traces, etc. auch zur Laufzeit bekomme, ohne Debugger oder Libs. Über Sinn und Zweck scheiden sich hier bekanntlich die Gemüter. Jedenfalls zum Problem; ich habe folgende Makros (vereinfacht):

    #define GUARD try { \
       StackMark guardStackMark(__FILE__, __LINE__, __FUNCTION__)
    
    #define UNGUARD \
       } \
       catch ...
    
    // Beispieleinsatz
    void foobar()
    {
       GUARD;
    
       ... code ...
    
       UNGUARD;
    }
    

    Knackpunkt ist, dass sobald ich 2 GUARD/UNGUARDs in einer Funktion habe bekomme ich eine shadowed/doppelt definierte Variable. Ich habe schon über oeStackMark##__LINE__ nachgedacht. Leider macht mir hier der Compiler einen Strich durch die Rechnung. Hat einer einen kreativen Einfall außer die Nutzung eines solchen Makros einzustellen? 😉

    Grüße,
    Christoph



  • hilft #undef ?



  • Der Ansatz oeStackMark##__LINE__ ist schon der richtige Weg, nur wenn du das direkt so schreibst, verkettet der Präprozessor erst und sucht dann nach neuen Ersetzungen. Da benötigst du eine Zwischenstufe:

    #define CONCAT(a,b) a##b
    #define GUARD try {\
      StackMark CONCAT(guard,__LINE__)(__FILE__,__LINE__,__FUNCTION__)
    


  • Die Frage ist aber: Wofür sollte man das 2 mal oder öfter in einer Funktion brauchen?!



  • Pellaeon: Nein #undef kann da nicht helfen. Da der Präprozessor zustandlos ist und auch nur einen pass macht, d.h. man kein Makro in einem Makro definieren kann lassen sich auch keine Schleifen erzeugen.

    hmmmmmmmmmmm: Die Frage nach dem Sinn ist ja immer relativ. Mein kleines "ich" in mir sagt mir, dass ich sowas brauch. Wenn es deins nicht tut, so hoffe ich, dass dein "ich" tollerant genug ist um mein Streben nach einer Lösung zu ertragen 😉

    CStoll: Ich bin mir nicht sicher ob er Fehler bei mir liegt. Deine Variante führt zu CONCAT(guardStackMark, __LINE): "guardStackMark__LINE__", was exakt das gleiche wie guardStackMark##__LINE__ ist, leider. Ich fürchte fast, dass __LINE__ der Compiler und nicht der Präprozessor auflöst. Wenn du GCC, wie ich benutzt, kannst du dir den Compiler-Output mal per "-save-temps" anzeigen lassen. Das ist echt knifflig 🙂

    Grüße,
    Christoph



  • Nein, ich arbeite mit Visual Studio 😉 *geht nachdenken*



  • Ok man kann sich mal dick irren. Die 51 war mal __LINE__.

    #define OE_GUARD_CONCAT(a, b) a##b
    
    #define OE_GUARD \
    	try { \
    	::OE::Core::StackMark OE_GUARD_CONCAT(__LINE__,guardStackMark)( \
    							__FILE__, __LINE__, __FUNCTION__)
    
    try { ::OE::Core::StackMark __LINE__guardStackMark( "Main/Src/Regressiontest/Tests/OECoreExceptionTest.h", 51, __FUNCTION__);
    

    Sehr doof, dass er es nicht auflöst.

    Grüße,
    Christoph



  • yellowsheep schrieb:

    Die Frage nach dem Sinn ist ja immer relativ. Mein kleines "ich" in mir sagt mir, dass ich sowas brauch. Wenn es deins nicht tut, so hoffe ich, dass dein "ich" tollerant genug ist um mein Streben nach einer Lösung zu ertragen 😉

    Das ist ja mal eine recht diplomatische Formulierung. Sieht man nicht sehr oft auf diesem Forum. 👍

    yellowsheep schrieb:

    CStoll: Ich bin mir nicht sicher ob er Fehler bei mir liegt. Deine Variante führt zu CONCAT(guardStackMark, __LINE): "guardStackMark__LINE__", was exakt das gleiche wie guardStackMark##__LINE__ ist, leider. Ich fürchte fast, dass __LINE__ der Compiler und nicht der Präprozessor auflöst. Wenn du GCC, wie ich benutzt, kannst du dir den Compiler-Output mal per "-save-temps" anzeigen lassen. Das ist echt knifflig 🙂

    Grüße,
    Christoph

    Der Preprocessor werkelt wie folgt:

    • 1. Erweiteren der Funktionsmarkos im Source code (Tokenpasting mit ## gehört hierzu).
    • 2. Erweiteren der Ersetzungsmakros im jetztigen Source code
    • 3. Fals etwas ersetzt wurde springe zu 1. zurück.

    Dieses Model ist stark vereinfacht. Zum Beispiel passt die Tatsache, dass ein Makro nur über einen gewissen Teil des Quelltextes definiert ist nicht in dieses Model. Das gleiche Problem ist die Tatsache, dass Makros leider nicht recursive ersetzt werden können. Für unser Problem reicht es aber.

    Dies heißt bei

    #define CONCAT(a,b) a##b
    #define GUARD try {\
      StackMark CONCAT(guard,__LINE__)(__FILE__,__LINE__,__FUNCTION__)
    

    Macht er folgendes

    GUARD
    
    try {\
      StackMark CONCAT(guard,__LINE__)(__FILE__,__LINE__,__FUNCTION__)
    
    try {\
      StackMark guard__LINE__(__FILE__,__LINE__,__FUNCTION__)
    

    Wenn du jetzt aber das Makro wie folgt definiert hättest

    #define GUARD_REAL(line) try {\
      StackMark guard ## line(__FILE__,__LINE__,__FUNCTION__)
    #define GUARD_EXPAND(line) GUARD_REAL(line) // Hiermit wird Etappe 2 zwischengeschoben
    #define GUARD GUARD_EXPAND(__LINE__)
    

    Dann würde es deine Erwachtungen erfüllen.



  • Ben04, deine Lösung ist absolut korrekt und faszinierend zugleich! 🙂

    Vielen Dank!

    Grüße,
    Christoph


Anmelden zum Antworten