Alas, you can't do recursion in the preprocessor, but you can kinda-sorta make a "apply macro to all arguments to this variadic macro" macro.
A useful idea is that when calling a function-style macro, the () can be part of the argument. This allows you to do some funky things. In your specific example...
First, a macro that tells us how many arguments are in a __VA_ARGS__
pack:
#define NUM_ARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
#define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
Next, a macro that applies a given macro to every element of a __VA_ARGS__
pack independently:
#define FOREACH(MACRO, ...) FOREACH_(NUM_ARGS(__VA_ARGS__), MACRO, __VA_ARGS__)
#define FOREACH_(N, M, ...) FOREACH__(N, M, __VA_ARGS__)
#define FOREACH__(N, M, ...) FOREACH_##N(M, __VA_ARGS__)
#define FOREACH_1(M, A) M(A)
#define FOREACH_2(M, A, ...) M(A) FOREACH_1(M, __VA_ARGS__)
#define FOREACH_3(M, A, ...) M(A) FOREACH_2(M, __VA_ARGS__)
#define FOREACH_4(M, A, ...) M(A) FOREACH_3(M, __VA_ARGS__)
#define FOREACH_5(M, A, ...) M(A) FOREACH_4(M, __VA_ARGS__)
#define FOREACH_6(M, A, ...) M(A) FOREACH_5(M, __VA_ARGS__)
#define FOREACH_7(M, A, ...) M(A) FOREACH_6(M, __VA_ARGS__)
#define FOREACH_8(M, A, ...) M(A) FOREACH_7(M, __VA_ARGS__)
// Extend in the obvious way for as many iterations as needed.
A simple "stringify one thing" macro (that makes sure its argument expands):
#define STRINGIFY_(X) #X
#define STRINGIFY(X) STRINGIFY_(X)
And now let's put all the pieces together to make a nice example that builds a string array:
#define STRINGIFY_ALL(...) FOREACH(STRINGIFY, __VA_ARGS__)
#define COMMA(X) X,
#define COMMA_STRINGIFY(X) COMMA(STRINGIFY(X))
#define STRING_LITERAL_ARRAY(...) const char* STUFF[ NUM_ARGS(__VA_ARGS__) ] = { FOREACH(COMMA_STRINGIFY, __VA_ARGS__) };
STRING_LITERAL_ARRAY(I, AM, A, POTATO);
// Will yield:
const char* STUFF[ 4 ] { "I", "AM", "A", "POTATO" };
Hopefully you can see how you can write different "lambda-like" macros to use with FOREACH to do all sorts of exciting things.