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 Parameterx, y, z
angibt, das Macro daraus beim Nutzen von z.B.Type_##
dannType_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
undType_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; }
-
@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 nurint
entgegen. Das wird aber gewährleistet, weil ja zu dem ArgumentPosition
der PostfixFooBar
concateniert wird, womit das Argument zuPositionFooBar
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; }
-
@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 mitBOOST_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 Funktionfoo
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ürvoid 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) beiMACRO
hinzu.
-
@Th69 Vielen Dank, es funktioniert.
-