Variadic Macros mit Concatenation benutzen



  • Hallo,
    ist es möglich, unter gcc variadic Macros mit Concatenation zu benutzen? Intuitiv hätte ich gesagt, man würde es einfach so machen:

    void function(...);
    
    #define foo(uint8_t argCount, ...)\
        function(Type_##__VA_ARGS__);
    

    Das geht jedoch nicht, wie ich man hier erfahren kann.:

    If you write

    #define eprintf(format, …) fprintf (stderr, format, ##__VA_ARGS__)
    

    and the variable argument is left out when the eprintf macro is used, then the comma before the ‘##’ will be deleted. This does not happen if you pass an empty argument, nor does it happen if the token preceding ‘##’ is anything other than a comma.

    Gibt es also eine möglichkeit, ## auf jedes einzelne Element der Argumente anzuwenden? So dass, wenn man zum Beispiel als Parameter x, y, z angibt, das Macro daraus beim Nutzen von z.B. Type_## dann Type_x, Type_y, Type_z macht?

    Es muss nicht zwingend ein variadic Macro rauskommen, es würde auch reichen, wenn einfach (in diesem Beispie) dreimal nacheinander Type_x, Type_y und Type_z als einziger Parameter übergeben wird. Das liegt daran, dass ich vor den Macro Deklarationen schon

    #define Type_x 1
    #define Type_y 2
    #define Type_z 3
    

    geschrieben habe, so dass man sie auch einzeln übergeben könnte.

    Das Macro, das ich suche, sollte also ungefähr auf eine von den Folgenden beiden Weisen genutzt werden können(Pseudocode):

    void func(int type);
    
    #define foo(...)\
       for each element in __VA_ARGS__\
          func(Type_##element);
    

    oder

    void func(uint8_t argCount, ...);
    
    #define foo(argCount, ...)\
       func(argCount, CONCATENATE_VA_ARGS(Type_, __VA__ARGS__));
    

    Vielen Dank für eure Hilfe 🙂



  • @daniel
    In C übergibt man an Funktionen immer nur Objekte und niemals Typen, so wie du es zeigst.
    Für sowas Dynamisches würde ich stdarg.h nehmen und nicht VA_ARGS, denn stdarg.h ist zwar auch kein ISO-C Standard aber deutlich weiter verbreitet als die GCC-Extension VA_ARGS.
    Im Übrigen hat bei Makros immer nur der Präprozessor die Hand drauf, bei stdarg.h immer der Compiler und der prüft sehr viel genauer.

    #include <stdio.h>
    #include <stdarg.h>
    
    void foo( int w, ... )
    {
      va_list x;
      va_start( x, w );
      while(w--)
        puts(va_arg( x, char* ));
      va_end( x );
    } 
    
    int main()
    {
        foo(3, "1","2","3");
        return 0;
    }
    

    https://onlinegdb.com/By9GbNmfU



  • @Wutz Achso nein ich möchte auch keine Typen übergeben, sondern nur int. Der "Type_" Prefix war nur irgendein Beispiel für etwas, das man vor jedes Argument stellen möchte. Um etwas klarer zu machen, was ich meine, zeige ich mal ein Beispiel, wie man es nutzen können sollte.

    #define PositionFooBar 1
    #define VelocityFooBar 2
    
    typedef struct
    {
        int x, y;
    }Position;
    
    typedef struct
    {
        int x, y;
    }Velocity;
    
    
    void function(uint8_t argCount, ...)
    {
        //alle argumente werden vom Typ `int` sein, weil durch das Concatenieren die Argumente durch die bereits definierten Zahlen ersetzt werden
    }
    
    #define foo(uint8_t argCount, ...)\
        function(argCount, CONCATENATE_EACH_ELEMENT(__VA_ARGS__));
    
    int main()
    {
        foo(2, Position, Velocity);
    }
    

    Intern soll Position mit dem Wert von PositionFooBar assoziiert werden. Dafür brauche ich ein Macro, welches für jeden Wert von den vielen, die übergeben werden können, den Postfix FooBar hinzufügt, damit diese an eine Funktion übergeben werden können. Diese Funktion nimmt aber nur int entgegen. Das wird aber gewährleistet, weil ja zu dem Argument Position der Postfix FooBar concateniert wird, womit das Argument zu PositionFooBar wird, was ja als `int``definiert ist.

    Ich glaube ich habe durch die ganzen Infos nur Verwirrung gestiftet. Ich brauche einfach ein Macro, dass jedes einzelne Element eines variadic Arguments concateniert, anstatt nur das erste.



  • Du denkst viel zu statisch, nämlich in Makros. Makros sind Scheiße, eben weil sie per Definition nur statischen (also Compilezeit)Fokus haben, ein Programm lebt aber von seiner Dynamik - immer und überall.

    Das einzig Statische an diesem Programm sind die Typen und deren Funktionsarray.
    Mit einer Funktion pro Typ kannst du viel besser dynamisch arbeiten und auf deren Eigenarten eingehen.

    #include <stdio.h>
    
    enum {POSITION,VELOCITY};
    
    typedef struct { int x, y; } Position;
    typedef struct { double x, y; } Velocity;
    
    void P(Position*p){printf("Position %d %d\n",p->x,p->y);}
    void V(Velocity*v){printf("Velocity %f %f\n",v->x,v->y);}
    
    void (*func[])(void*)={P,V};
    
    int main()
    {
        Position p={1,2};
        Velocity v={3,4};
        
        func[POSITION](&p);
        func[VELOCITY](&v);
        
        return 0;
    }
    

    https://onlinegdb.com/S1ZMDUQfI



  • @Wutz Vielen Dank für deine Antworten im Übrigen 🙂 . Ich möchte aber keine Instanzen übergeben, sondern nur den Typ registrieren. Außerdem hängt der Ort, wo die Signaturen der Typen gespeichert werden, vom ersten Parameter ab. Außerdem möchte ich sehr viele Typen auf einmal übergeben. Und das auch viele Male, weil es ja viele verschiedene Möglichkeiten für den ersten Parameter gibt.
    Wie gesagt, möchte ich verschiedene Typen intern repräsentieren, und mit einer Nummer, weil diese für andere Dinge sehr wichtig ist. Daher bräuchte ich nur eine Möglichkeit, viele Typen auf einmal zu übergeben und diesen dann eine Nummer zuzuordnen. Wenn man nur einen übergibt, kann man ## benutzen, bei vielen Argumenten nicht. Kann ich va_list auch in Macros benutzen? Das würde der Lösung etwas näher kommen



  • Schau dir mal Boost Preprocessor an und deren Funktionalität bzgl. Variadic Macros.
    Dort gibt es z.B. BOOST_PP_SEQ_FOR_EACH, s.a. Appendix A - An Introduction to Preprocessor Metaprogramming (Übersicht "A.4.5.1 Sequences" fast ganz unten).



  • Hab eine Lösung gefunden.

    #define BX_foreach(Join,What, ...) BX_foreach_(BX_argc(__VA_ARGS__), Join, What, __VA_ARGS__)
    
    #define BX_foreach_(N, Join, What, ...) BX_paste(BX_cat(BX_foreach_, N)(Join, What, __VA_ARGS__))
    #define BX_cat(X,Y)  BX_cat_(X,Y) //{{{
    #define BX_cat_(X,Y) X##Y //}}}
    #define BX_call_first(Fn,...) Fn ( __VA_ARGS__ )
    #define BX_paste(...) __VA_ARGS__
    
    #define BX_argc(...) BX_argc_(X,__VA_ARGS__) //{{{
    #define BX_argc_(...) BX_argc__(,__VA_ARGS__,8,7,6,5,4,3,2,1,0,0)
    #define BX_argc__(_,_0,_1,_2,_3,_4,_5,_6,_7,_8,Cnt,...) Cnt //}}}
    #define BX_foreach_1(Join, What,  x) BX_call_first(What,  x)
    #define BX_foreach_2(Join, What,  x,...)BX_call_first(What,x) Join BX_foreach_1(Join, What,  __VA_ARGS__)
    #define BX_foreach_3(Join, What,  x,...)BX_call_first(What,x) Join BX_foreach_2(Join, What,  __VA_ARGS__)
    #define BX_foreach_4(Join, What,  x,...)BX_call_first(What,x) Join BX_foreach_3(Join, What,  __VA_ARGS__)
    #define BX_foreach_5(Join, What,  x,...)BX_call_first(What,x) Join BX_foreach_4(Join, What,  __VA_ARGS__)
    #define BX_foreach_6(Join, What,  x,...)BX_call_first(What,x) Join BX_foreach_5(Join, What,  __VA_ARGS__)
    #define BX_foreach_7(Join, What,  x,...)BX_call_first(What,x) Join BX_foreach_6(Join, What,  __VA_ARGS__)
    

    Das kann so benutzt werden:

    #define print(x) printf("%i", x)
    
    int main()
    {
        BX_foreach(;,print, 1, 2, 3, 4)
    }
    


  • @Th69 Danke, sehr interressante Bibliothek



  • @Th69 Das Problem an dem BX_foreach ist, dass man abgesehen von den Elementen in __VA_ARGS__ keine anderen Daten an die Funktion übergeben kann. Deine lösung mit BOOST_PP_SEQ_FOR_EACH_I(macro, data, seq) scheint schon zusätzliche Daten an die Funktion übergeben zu können. Aus der Dokumentation wird mir aber nicht ganz klar, wie ich das Macro zu nutzen habe. könntest du mir vielleicht ein Beispiel zeigen, bei dem auch zusätzliche Daten an die Funktion übergeben werden können? Wie muss der Kopf der Funktion foo aussehen, wenn ich einerseits bestimmte daten und zusätzlich ein element von __VA_ARGS__ übergeben will?

    #define bar(data, ...)\
       BOOST_PP_SEQ_FOR_EACH_I(foo data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) 
    


  • Bei BOOST_PP_SEQ_FOR_EACH gibt es doch ein Beispiel.
    Bei dir also so ähnlich (für void func(int type)):

    #define MACRO(r, data, elem) func(BOOST_PP_CAT(data, elem));
    
    #define bar(data, ...)\
       BOOST_PP_SEQ_FOR_EACH(MACRO, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
    
    bar(Type_, x, y, z)
    

    Und bei BOOST_PP_SEQ_FOR_EACH_I käme noch ein weiterer Parameter (Index) bei MACRO hinzu.



  • @Th69 Vielen Dank, es funktioniert.



  • 👍


Anmelden zum Antworten