Makro Parameter auswerten



  • Hallo an alle,

    ich habe gerade eine knifflige Aufgabe.
    Und zwar sollen drei Arrays mit int's angelegt werden.
    Dabei soll die gleiche Zahl entweder in einem oder mehreren
    der arrays vorhanden sein. Um das ganze tabellarisch zu
    gestalten soll fuer jede Zahl mit Hilfe eines Flags bestimmt
    werden in welchem array diese Zahl vorhanden sein soll
    ( siehe CRATE_ARRAY ) unten.
    Mein Problem ist nun, wie kann man anhand dieser uebergebenen
    Flags feststellen ob die Zahl im array landen soll oder nicht.
    Geht sowas ueberhaupt ?
    Hab hier schonmal nen vorab Entwurf:

    #define FLAG_1 	0x1
    #define FLAG_2 	0x2
    #define FLAG_3 	0x4
    
    /* Array definieren, die Zahlen sollen nur in den   */
    /* Arrays landen, die auch im Flag angegeben wurden */
    #define CRATE_ARRAY(F)              \
            F( 123, FLAG_1 | FLAG_2  )  \
            F( 456, FLAG_1 | FLAG_3  )  \
            F( 789, FLAG_2 | FLAG_3  )  \
    
    /* Hier auswerten ob diese Zahl in array 1 landen soll */
    #define DETECT_FLAG_1( ZAHL, FLAGS ) \
       ==> Hier Problem: Wenn das Flag FLAG_1 gesetzt ist, 
           soll das define die Zahl wiedergeben, ansonsten nichts
    
    /* In diesem Array sollen alle mit FLAG_1 landen */
    int MyArray_1[]
    {
       CRATE_ARRAY(DETECT_FLAG_1)
    };
    
    // ...hier die restlichen arrays 2 und 3...
    

    Danke schonmal im Voraus,
    viele Grüße
    Ulli


  • Mod

    "Unmöglich" ist ein starkes Wort. Aber es wird aufwändig. Einer der von dir gewünschten Teile, und zwar die Schreibweise FLAG_1 | FLAG_2 , dürfte sogar ganz unmöglich sein. Innerhalb von Makroexpansion kann nämlich nicht gerechnet werden, das heißt, das müsste selber in Form von Makros geschrieben werden a la

    BIT_OR(FLAG_1, FLAG_2)
    

    Und dann eben verschachtelt, falls man mehr als zwei Flags wünscht:

    BIT_OR(BIT_OR(FLAG_1, FLAG_2), FLAG_3)
    

    Wenn man damit auskommt, dann kann man das gewünscht umsetzen. Aber ziemlich aufwändig, weil man dann nochmal eine solche Rechnung machen müsste, um die ver-ODER-ten Werte wieder auszuwerten.

    Gegenvorschläge:

    F( 123, 1, 1, 0 )  \
            F( 456, 1, 0, 1 )  \
            F( 789, 0, 1, 1 )
    

    Ist doch auch ganz hübsch, oder? Damit wäre eine Lösung schon wesentlich einfacher. Oder

    F( 123, 110 )  \
            F( 456, 101 )  \
            F( 789, 011 )
    

    was nahezu trivial wäre.

    Überhaupt finde ich das mit dem F etwas ungünstig. Wäre so etwas wie folgendes nicht schöner?

    #define CREATE_ARRAY(INDEX)      \
            EVAL( 123, 110, INDEX )  \
            EVAL( 456, 101, INDEX )  \
            EVAL( 789, 011, INDEX )
    
       int array1[] = {CREATE_ARRAY(1)};
       int array2[] = {CREATE_ARRAY(2)};
       int array3[] = {CREATE_ARRAY(3)};
    

    Was ich die ganze Zeit noch nicht beachtet habe ist das Problem mit den Kommas. Die erzeugte Liste muss schließlich mit einer Zahl abgeschlossen werden, also kann man nicht plump nach jeder Zahl ein Komma setzen. Aber dafür gibt es sicherlich eine Lösung, wenn ich etwas darüber nachdenke. Ich wollte zunächst einmal erläutern, was möglich ist und was nicht und was einfach ist und was nicht.



  • SeppJ schrieb:

    Was ich die ganze Zeit noch nicht beachtet habe ist das Problem mit den Kommas. Die erzeugte Liste muss schließlich mit einer Zahl abgeschlossen werden,

    vielleicht hab ich die Sache falsch verstanden, aber im K&R steht dazu "A list may end with a comma[...]" (A8.7, S.218 2nd ed.). Der gcc frisst das ohne Probleme und ohne Warning selbst mit -Wall.

    int f(int a)
    {
    	int arr[]={1,2,3,};
    	return arr[a];
    }
    

    (das return-Zeugs entfernt die Warnung unused-variable).



  • Ich schließe die gern mit Kommas ab, vor allem, wenn ich Zeilen habe, dann kann ich die nachher hübsch zeilenweise umsortieren.

    int arr[]={
       1,2,3,
       7,8,9,
       4,5,6,
    };
    

    (das return-Zeugs entfernt die Warnung unused-variable).

    glaube,

    void f(int a)
    {
        (void)a;//unused parameter
        int arr[]={1,2,3,};
        (void)arr;//unused variable
    }
    

    ist der übliche Weg dadafür.


  • Mod

    schnock schrieb:

    SeppJ schrieb:

    Was ich die ganze Zeit noch nicht beachtet habe ist das Problem mit den Kommas. Die erzeugte Liste muss schließlich mit einer Zahl abgeschlossen werden,

    vielleicht hab ich die Sache falsch verstanden, aber im K&R steht dazu "A list may end with a comma[...]" (A8.7, S.218 2nd ed.).

    Ja, irgendwie hatte ich das im Hinterkopf, aber war zu faul es nachzuschlagen. Da ich es noch nie irgendwo gesehen habe, ging ich eher davon aus, dass es nicht erlaubt ist.

    Das macht die Sache natürlich noch einmal wesentlich einfacher.



  • Hallo,

    vielen Dank noch für eure Infos.
    Leider hat sich noch ein neues Problem aufgetan,
    und zwar muesste ich innerhalb des defines noch
    eine #if Abfrage einbauen, was natürlich auch nicht geht 🙄
    Oder weiss da einer von euch eine tricky Lösung dafür ?

    #define CRATE_ARRAY(F)              \
            F( 123, FLAG_1 | FLAG_2  )  \
    #if BLABLA                          \
            F( 456, FLAG_1 | FLAG_3  )  \
    #endif                              \
            F( 789, FLAG_2 | FLAG_3  )  \
    

    Viele Grüße
    Ulli


  • Mod

    Das ist immer noch das gleiche Problem, das ich bereits beschrieben habe (und worauf du leider nicht weiter eingehst), bloß in einer anderen Farbe bemalt. Der Präprozessor ist zwar an sich in der Lage, einfache Rechnungen durchzuführen und kennt auch bedingte Anweisungen, jedoch ist innerhalb von Makros nichts davon aktiv. In Makroauswertungen funktionieren nur andere Makros. Entsprechend muss man auch dies als Makro schreiben. Beispielsweise ist es möglich, so etwas zu machen:

    IF(BEDINGUNG, A, B)
    

    Das IF ist dabei selber ein Makro, das zu A expandiert, wenn die Bedingung 1 ist oder zu B, wenn sie 0 ist. Entsprechend könnte das bei dir dann so aussehen:

    #define CREATE_ARRAY(F)                  \
                 F( 123, FLAG_1 | FLAG_2  )  \
    IF(BLABLA,   F( 456, FLAG_1 | FLAG_3  ), \
                 F( 789, FLAG_2 | FLAG_3  )) \
    


  • CUlli schrieb:

    gleiche Zahl entweder in einem oder mehreren
    der arrays vorhanden sein. Um das ganze tabellarisch zu
    gestalten soll

    Geht sowas ueberhaupt ?
    Hab hier schonmal nen vorab Entwurf:

    Ich fürchte, der Entwurf verwirrt mich zu sehr.

    Kannste nochmal ganz einfach schreiben, was die Eingangsdaten sind und was die Ausgangsdaten? So, als wenn keine Makros verwendet werden müssten, sondern Du einem Mietkoch oder BWL-er für 8,50€/h damit beauftragen würdest, der das dann per Hand macht. Fürchte ein X-Y-Z-Problem, und unsere Beantworter verrennen sich in Y, obwohl Z gesucht ist.



  • Hallo Volkard und alle anderen,
    ich denke du hast recht. Ich hätte zuerst die Ausgangslage
    genauer beschreiben sollen. Ich habe folgende Listen:

    // in Liste 1 sind alle Zahlen drin
    int Liste1[] =
    {
       123,
       234,
       456
    };
    
    // in Liste 2 soll die 2. Zahl nicht drin sein
    int Liste2[] =
    {
       123,
       // 234,
       456
    };
    
    // in Liste 3 soll die 1. und 3. Zahl nicht drin sein
    int Liste3[] =
    {
       // 123, 
       234,  
       // 456  
    };
    
    // struct um die Listen in nachfolgendes array zu stopfen
    struct ListenEintrag
    {
       int* pListe;
       int nLaenge;
    };
    
    // array mit allen Listen
    int nMax = 3;
    
    struct ListenEintrag MyList[] =
    {
       { Liste1, sizeof(Liste1) / sizeof(int) },
       { Liste2, sizeof(Liste2) / sizeof(int) },
       { Liste3, sizeof(Liste3) / sizeof(int) },
    };
    
    // so, fast fertig, mit nachfolgender 
    // Funktion wird immer die entsprechende Liste ausgegeben
    void showList( int nListeNr )
    {
       int i=0;
    
       for( i=0; i < MyList[nListeNr].nLaenge; i++ )	
       {
          printf("Liste Nr%d, Eintrag %d = %d \n", nListeNr+1, i+1, MyList[nListeNr].pListe[i] );
       }
    }
    
    // Und jetzt alle Listen ausgeben
    void main()
    {
       int i=0;
    
       for( i=0; i < nMax; i++ )
       {
          showList(i);
       }
    }
    

    Das Anlegen der Listen (die in Wirklichkeit viel grösser sind)
    ist eben auf diese Art unschön. Viel schöner wäre eben sowas:

    //  Zahl   Liste_1    Liste_2    Liste_3    
            123,      1         1           0   
            234,      1         0           1
            456       1         1           0
    #if BLALBA
            567       1         0           1
    #endif
    

    vielen Dank nochmal für eure Geduld und eure Ideen 👍


  • Mod

    Und jetzt erklärst du am besten noch, wieso das mittels des Präprozessors umgesetzt werden soll. Das klingt mir doch sehr stark nach etwas, das eine Konfigurationsdatei oder Datenbank viel einfacher, besser und flexibler lösen könnte.



  • Das ganze MUSS beim start sofort verfügbar sein. Das ganze läuft auf
    einem kleinen Mikrocontroller auf dem es keine Datenbanken, Dateien
    oder ähnliches gibt. Dazu muss alles noch Laufzeitoptimiert sein.



  • Achso, hab ich ganz vergessen, dynamisches Speicher allokieren ist
    natürlich auch nicht möglich.


  • Mod

    Um mal zu zeigen, wie so etwas prinzipiell aussieht (wichtige Hinweise folgen!):

    #define COMMA() ,
    #define EMPTY() 
    
    #define IF_INDEX_EVAL_1_000(VALUE) EMPTY
    #define IF_INDEX_EVAL_1_001(VALUE) EMPTY
    #define IF_INDEX_EVAL_1_010(VALUE) EMPTY
    #define IF_INDEX_EVAL_1_011(VALUE) EMPTY
    #define IF_INDEX_EVAL_1_100(VALUE) VALUE COMMA
    #define IF_INDEX_EVAL_1_101(VALUE) VALUE COMMA
    #define IF_INDEX_EVAL_1_110(VALUE) VALUE COMMA
    #define IF_INDEX_EVAL_1_111(VALUE) VALUE COMMA
    
    #define IF_INDEX_EVAL_2_000(VALUE) EMPTY
    #define IF_INDEX_EVAL_2_001(VALUE) EMPTY
    #define IF_INDEX_EVAL_2_010(VALUE) VALUE COMMA
    #define IF_INDEX_EVAL_2_011(VALUE) VALUE COMMA
    #define IF_INDEX_EVAL_2_100(VALUE) EMPTY
    #define IF_INDEX_EVAL_2_101(VALUE) EMPTY
    #define IF_INDEX_EVAL_2_110(VALUE) VALUE COMMA
    #define IF_INDEX_EVAL_2_111(VALUE) VALUE COMMA
    
    #define IF_INDEX_EVAL_3_000(VALUE) EMPTY
    #define IF_INDEX_EVAL_3_001(VALUE) VALUE COMMA
    #define IF_INDEX_EVAL_3_010(VALUE) EMPTY
    #define IF_INDEX_EVAL_3_011(VALUE) VALUE COMMA
    #define IF_INDEX_EVAL_3_100(VALUE) EMPTY
    #define IF_INDEX_EVAL_3_101(VALUE) VALUE COMMA
    #define IF_INDEX_EVAL_3_110(VALUE) EMPTY
    #define IF_INDEX_EVAL_3_111(VALUE) VALUE COMMA
    
    #define IF_INDEX_INDIRECTION(INDEX, VALUE, LIST) \
      IF_INDEX_EVAL_ ## INDEX ## _ ## LIST(VALUE)
    #define IF_INDEX(INDEX, VALUE, LIST) IF_INDEX_INDIRECTION(INDEX, VALUE, LIST)()
    
    #define IF_EVAL_0(...) 
    // Für den Fall, dass die Condition nicht gesetzt ist, interpretiere ich sie als 0:
    #define IF_EVAL_(...)
    #define IF_EVAL_1(...)  __VA_ARGS__
    #define IF_EVAL_INDIRECTION(COND, ...) IF_EVAL_ ## COND( __VA_ARGS__)
    #define IF_CONDITION(COND, ...) IF_EVAL_INDIRECTION(COND, __VA_ARGS__)
    
    // Bis hier hin war alles nur Vorspiel. hier ist deine Tabelle:
    
    #define CREATE_ARRAY(INDEX)                      \
                           IF_INDEX(INDEX, 123, 110) \
                           IF_INDEX(INDEX, 456, 101) \
      IF_CONDITION(BLABLA, IF_INDEX(INDEX, 789, 011))
    
    // Und so könnte dann die Benutzung im Code aussehen:
    
    #define BLABLA 1
    
    int array1[] = { CREATE_ARRAY(1) };
    int array2[] = { CREATE_ARRAY(2) };
    int array3[] = { CREATE_ARRAY(3) };
    
    #undef BLABLA
    #define BLABLA 0
    
    int array4[] = { CREATE_ARRAY(1) };
    int array5[] = { CREATE_ARRAY(2) };
    int array6[] = { CREATE_ARRAY(3) };
    

    Expandiert zu:

    int array1[] = { 123 , 456 , };
    int array2[] = { 123 , 789 , };
    int array3[] = { 456 , 789 , };
    
    int array4[] = { 123 , 456 , };
    int array5[] = { 123 , };
    int array6[] = { 456 , };
    

    Hinweise und Erläuterungen:

    • Dies ist mal eben schnell von mir zusammengeschustert. Präprozessormakros schreibe ich nicht regelmäßig. Es gibt daher sicherlich viele Tricks und Best-Practices, die ich nicht beachtet habe.
    • Das Grundprinzip wird jedoch letztlich das gleiche bleiben: Irgendwo muss so ein Monstrum wie meine Zeilen 4-29 stehen. Hier zeige ich es explizit und sehr spezialisiert auf die konkrete Problemstellung. Normalerweise wird so etwas irgendwo schön versteckt in einem anderen Header stehen und optimalerweise auch etwas abstrakter und somit vielfältiger anwendbar als mein Beispiel.
    • Die Kommas haben mich am Ende doch fast in den Wahnsinn getrieben, weil der Präprozessor Kommas immer als Trennzeichen interpretiert. Man muss daher sehr genau aufpassen, wann man wie die Kommas expandiert. Um halbwegs die Syntax gewährleisten zu können, die du dir gewünscht hast, fiel mir nach einigen erfolglosen Versuchen nichts besseres ein, als variadische Makros einzusetzen. Die gibt es offiziell zwar erst seit C99 (bzw. C++11), aber sie sollten in jedem gängigen Präprozessor funktionieren, selbst wenn der Compiler vielleicht nur C89/C++98 kann.
    • All dies gibt es schon! Ich bin schließlich nicht der Erste mit diesen Ideen (ehrlich gesagt habe ich sie mir abgeguckt :p ). Boost Preprocessor ist zwar eigentlich eine C++-Bibliothek, da der Präprozessor aber beiden Sprachen weitgehend gemeinsam ist, funktioniert diese Makrosammlung auch zu 100% in C. Die machen genau das, was ich an meinem Code bemängelt habe, denn die Macher haben viel mehr Erfahrung und Manpower als ich. Wenn du Boost benutzt, dann werden sich sicherlich schönere Lösungen finden lassen, wo ich bei mir dreckige Tricks benutzt hab (weil die dreckigen Tricks dann in den Boost-Headern versteckt sind oder weil die Boost-Makros einfach besser geschrieben sind als meine)


  • Hallo Seppj,

    vielen Dank, für deine Tipps. Super, dass du dir so viel
    Zeit genommen hast 👍 . Den link auf die boost Seite
    find ich ebenfalls hervorragen. Denke da kann ich meinen
    Horizont noch ganz gut erweitern.

    Gruß Ulli


Log in to reply