Expansionsregeln beim C++-Präprozessor



  • Hallo.

    Ich habe immer noch ernsthafte Probleme damit, den C++-Präprozessor zu verstehen. Ich versuche das folgende Codebeispiel lauffähig zu machen:

    #define If_Helper_0(True)
    #define If_Helper_1(True) True
    #define If(Bit, True) If_Helper_ ## Bit (True)
    
    #define Id2(Arg1, Arg2) Arg1, Arg2
    
    int main()
    {
    	If(1, Id2(int a, b));
    	a = b = 42;
    }
    

    Wieso dieses Listing nicht funktioniert, das folgende jedoch schon, ist mir schleierhaft.

    #define Id1(Arg) Arg
    #define Id2(Arg1, Arg2) Arg1, Arg2
    
    int main()
    {
    	Id1(Id2(int a, b));
    	a = b = 42;
    }
    

    Gruss.

    PS.: Bitte nicht stumpf Boost.PP vorschlagen, ich möchte es ja auch verstehen.

    Edit: Die Lösung sollte in C++03 funktionieren.



  • Erstmal ein möglicher Fix:

    #define If_Helper_0(...) __VA_ARGS__
    #define If_Helper_1(...) __VA_ARGS__
    #define If(Bit, True) If_Helper_ ## Bit (True)
    // oder #define If(Bit, True) If_Helper_ ## Bit ((True))
    // das führt aber zu einem Klammerpaar um das Resultat
    
    #define Id2(Arg1, Arg2) Arg1, Arg2
    
    int main()
    {
        If(1, Id2(int a, b)); // expandiert zu int a, b
        a = b = 42;
    }
    

    Ich glaube das hat irgendwas mit dem Argument Prescan zu tun, aber der Preprozessor ist auch nicht meine Stärke 😉



  • Relativ einfach - du verwendest If(Bit,True) mit If(1,Id2(int a,b)) .
    Aber was ist Id2? Doch nur eine Ersetzung der zwei übergebenen Argumente.

    Nach der ersten Ersetzung hast du dann If(1,int a,b) stehen. Jetzt ersetzen wir If , sodass If_Helper_1(int a,b) rauskommt. Aber If_Helper_1 übernimmt nur ein Argument, nicht zwei. Und da kommt der Präprozessor ins Schleudern.

    Möglicher Fix wurde schon gepostet.



  • Ups, sorry. Wieder einmal vergessen zu erwähnen, dass ich bei C++03 bleiben möchte. Hab's im OP hinzugefügt.

    Tim06TR schrieb:

    // oder #define If(Bit, True) If_Helper_ ## Bit ((True))
    // das führt aber zu einem Klammerpaar um das Resultat
    

    Das Problem hierbei ist, dass das Klammerpaar bei beispielsweise (Template-)Parameterlisten nicht akzeptiert wird. Und das Makro zum Entfernen des Klammernpaares weist (unter C++03) die selbe Problematik auf.

    dachschaden schrieb:

    Relativ einfach - du verwendest If(Bit,True) mit If(1,Id2(int a,b)) .
    Aber was ist Id2? Doch nur eine Ersetzung der zwei übergebenen Argumente.

    Nach der ersten Ersetzung hast du dann If(1,int a,b) stehen. Jetzt ersetzen wir If , sodass If_Helper_1(int a,b) rauskommt. Aber If_Helper_1 übernimmt nur ein Argument, nicht zwei.

    Und wieso funktioniert es dann bei If wenn da Id2 zuerst ersetzt wird? Dann würde man ja drei statt zwei Argumente übergeben. Und wieso funktioniert es bei Id1 wenn zuvor ja angeblich Id2 ersetzt wird?


  • Mod

    [cpp.subst]/1 schrieb:

    After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or
    followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.

    Die Argumentsubstitution geschieht erst in der replacement list für ein Funktionsmakro. Nachdem die Konkatenierung durchgeführt wurde werden alle Argumente für die Parameter ersetzt.

    #define If(Bit, True) If_Helper_ ## Bit (True)
    

    An If_Helper_* wird tatsächlich makro-freier Text übergeben. Daher werden's auch drei Argumente.

    Id1(Id2(int a, b))
    

    Id1 wird mit Id2(..) als Argument aufgerufen. Keine Kommas außerhalb des äußersten Klammerpaars, also nur ein Argument.
    Id2(int a, b) wird nicht sofort expandiert da es ein Argument ist.


Anmelden zum Antworten