*Not to be confused with having anything to do with Associative Arrays.
I know how to vectorize a function in C with macros to give results similar to the Mathematica's Map (or Apply) functionality. Namely apply a function to a list of arguments.
#define Apply( type, function, ...) \
{ \
void *Stop = (int[]){0}; \
type **List = (type*[]){__VA_ARGS__, Stop}; \
for( int i = 0; List[i] != Stop; ++i ) \
function( List[i] ); \
}
I can then do something like
#define FreeAllAtOnce(...) Apply( void, free, __VA_ARGS__ );
which has the effect that
free( Array1 );
free( Array2 );
free( Array3 );
is equivalent to
FreeAllAtOnce( Array1, Array2, Array3 );
I didn't make that up, I read about it in a book and have used it heavily since.
My question is: Can I do something similar to associatively combine an array via some binary function. For example take the GCD function. I want a function like:
GCD_all( a, b, c, d, e );
That has the same effect as
GCD( GCD( GCD( GCD( a, b ), c ), d ), e );
for any number of arguments.
I've tried to do this and have not been able to properly make it work. I'm also interested in the case where there may be additional parameters passed to the function. In the most generic sense I'm looking to do this with functions like:
Atype BinaryCombine( Atype a, Atype b, OtherType z, OtherType y )
so that I have a function
Atype BinaryCombineAll( Atype a, Atype b, Atype c, Atype d, OtherType z, OtherType y )
I hope that makes sense. Any ideas or help would be very appreciated!
Thanks.
The double-number-sign or token-pasting operator (##), which is sometimes called the merging or combining operator, is used in both object-like and function-like macros. It permits separate tokens to be joined into a single token, and therefore, can't be the first or last token in the macro definition.
Function-like macros can take arguments, just like true functions. To define a macro that uses arguments, you insert parameters between the pair of parentheses in the macro definition that make the macro function-like. The parameters must be valid C identifiers, separated by commas and optionally whitespace.
"Macros" in C are a simple string substitution (including substitution of arguments) done by the C pre-processor (on Linux, that would be cpp foo. c ) before compilation is done. Run just the C pre-processor on your source to see exactly how the macro is expanded. It doesn't exactly act like a function.
Remarks. The #define directive causes the compiler to substitute token-string for each occurrence of identifier in the source file. The identifier is replaced only when it forms a token. That is, identifier is not replaced if it appears in a comment, in a string, or as part of a longer identifier.
This requires rather tricky machinery (see this answer for more details), since you cannot normally have recursive macros in C:
#define _NUM_ARGS2(X,X5,X4,X3,X2,X1,N,...) N
#define NUM_ARGS(...) _NUM_ARGS2(0,__VA_ARGS__,5,4,3,2,1,0)
// be sure to add X6,X7,... and 6,7,... to support more arguments
#define GCD_OF_1(a) (a)
#define GCD_OF_2(a,b) GCD(a, b)
#define GCD_OF_3(a,b,...) GCD_OF_2(GCD_OF_2(a,b),__VA_ARGS__)
#define GCD_OF_4(a,b,...) GCD_OF_3(GCD_OF_2(a,b),__VA_ARGS__)
#define GCD_OF_5(a,b,...) GCD_OF_4(GCD_OF_2(a,b),__VA_ARGS__)
// in general:
// #define GCD_OF_N(a,b,...) GCD_OF_N-1(GCD_OF_2(a,b),__VA_ARGS__)
#define _GCD_OF_N3(N, ...) GCD_OF_ ## N(__VA_ARGS__)
#define _GCD_OF_N2(N, ...) _GCD_OF_N3(N, __VA_ARGS__) // helper macro to expand N
#define GCD_all(...) _GCD_OF_N2(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
int main(void)
{
GCD_all(a, b, c, d, e);
}
The gcc -E
produces output as:
int main(void)
{
GCD(GCD(GCD(GCD(a, b), c), d), e);
}
The NUM_ARGS
automagically finds the number of arguments. This way you obtain the "starting point" macro GCD_OF_N
for futher expanding.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With