8

*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.

amcalde
  • 203
  • 1
  • 4
  • I think you could have had some success with C++11 variadic templates – ForceBru Jun 16 '16 at 14:42
  • BTW, how are you going to identify how many arguments `function` can take? – ForceBru Jun 16 '16 at 15:01
  • @ForceBru, I am interested in staying within C. In my first example function only takes one argument. What I'm asking for is one that takes two (or more). – amcalde Jun 16 '16 at 15:04
  • So here's the question: how do you know how many arguments this function takes? `GCD_all(a,b,c,d)` might result in `gcd(a, b, gcd(c,d))` or whatever. I mean, you'll have to tell `GCD_all` how many arguments `gcd` takes. – ForceBru Jun 16 '16 at 15:07
  • I'm sure I'm not sure I understand you :). gcd takes exactly two arguments. You would have to make a new macro for each function individually. (I think). What I'm missing is the first part. Something like my Apply macro. – amcalde Jun 16 '16 at 15:15
  • Do you need `gcd` accept any type or something like `long` or `int` will suit? – ForceBru Jun 16 '16 at 15:27

1 Answers1

2

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.

Community
  • 1
  • 1
Grzegorz Szpetkowski
  • 36,988
  • 6
  • 90
  • 137
  • Thanks. It seems that you would need to do all these macros in advance, covering every possible case of inputs. I thought of another way to do it but it requires all the inputs to be pointers, not data. – amcalde Jun 20 '16 at 12:30