Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C-preprocessor: iteratively expand macro to comma-separated list

Using Paul Fultz II's solution in the post C-preprocessor recursive macro, I'd like to expand an unlimited number of parenthesized macro arguments, e.g.

#define MY_CHAIN (alpha) (beta) (gamma)

into a comma-separated list which can be passed to a variadic macro, e.g.

CHAIN_COMMA(MY_CHAIN) // alpha, beta, gamma

I'm able to expand into braces [alpha] [beta] [gamma] and delimit the list with everything I've tried except a comma, alpha :: beta :: gamma in the example below.

Here is my full (compiling) code:

#include <iostream>
using namespace std;

// unrelated macro utilities
#define SEE(expression) cout << #expression ": " << STR(expression) << endl;
#define CMD(function, ...) function(__VA_ARGS__)
#define STR(s) CMD(STR_, s)
#define STR_(s) #s

// concatenation
#define CAT(x, y) CAT_(x, y)
#define CAT_(x,y) x ## y // error from CHAIN_COMMA: passed 4 arguments

// surround each chain element with square brackets []
#define CHAIN_BRACE(chain) CAT(CHAIN_BRACE_1 chain, _END)
#define CHAIN_BRACE_1(x) [x] CHAIN_BRACE_2
#define CHAIN_BRACE_2(x) [x] CHAIN_BRACE_1
#define CHAIN_BRACE_1_END
#define CHAIN_BRACE_2_END

// separate each chain element with the scope operator ::
#define CHAIN_SCOPE(chain) CAT(CHAIN_SCOPE_0 chain, _END)
#define CHAIN_SCOPE_0(x) x CHAIN_SCOPE_1
#define CHAIN_SCOPE_1(x) :: x CHAIN_SCOPE_2
#define CHAIN_SCOPE_2(x) :: x CHAIN_SCOPE_1
#define CHAIN_SCOPE_0_END
#define CHAIN_SCOPE_1_END
#define CHAIN_SCOPE_2_END

// trouble here: can't separate chain elements with commas
#define CHAIN_COMMA(chain) CAT(CHAIN_COMMA_0 chain, _END) // error
#define CHAIN_COMMA_0(x) x CHAIN_COMMA_1
#define CHAIN_COMMA_1(x) , x CHAIN_COMMA_2
#define CHAIN_COMMA_2(x) , x CHAIN_COMMA_1
#define CHAIN_COMMA_0_END
#define CHAIN_COMMA_1_END
#define CHAIN_COMMA_2_END

// define a custom chain and save various forms of it
#define MY_CHAIN (alpha) (beta) (gamma)
#define MY_BRACES CHAIN_BRACE(MY_CHAIN) // [alpha] [beta] [gamma]
#define MY_SCOPES CHAIN_SCOPE(MY_CHAIN) // alpha :: beta :: gamma
#define MY_COMMAS CHAIN_COMMA(MY_CHAIN) // alpha , beta , gamma

int main() {
    SEE(MY_CHAIN);
    SEE(MY_BRACES);
    SEE(MY_SCOPES);
//  SEE(MY_COMMAS); // error: macro "CAT_" passed 4 arguments, but takes just 2
    return 0;
}

This outputs:

MY_CHAIN: (alpha) (beta) (gamma)
MY_BRACES: [alpha] [beta] [gamma]
MY_SCOPES: alpha :: beta :: gamma

I tried parenthesizing the comma-separated list but CAT won't append ) to _END. Any clever ideas to expand into alpha, beta, gamma?

like image 609
Taylor Nichols Avatar asked Mar 12 '18 12:03

Taylor Nichols


1 Answers

As the comma is both important to your output and is a syntactic element, you need to make a substitute comma for outputting.

#define COMMA() ,

We will also need some deferring functions so that COMMA isn't evaluated immediately.

#define EMPTY()
#define DEFER(id) id EMPTY()

Now we can redefine your two macros into

#define CHAIN_COMMA_1(x) DEFER(COMMA)() x CHAIN_COMMA_2
#define CHAIN_COMMA_2(x) DEFER(COMMA)() x CHAIN_COMMA_1

However, your SEE macro also doesn't like the commas that are placed, and so will error for having too many parameters passed.

You can see that the macro is performing correctly by looking at the output of the preprocessor with the -E option.

like image 139
unDeadHerbs Avatar answered Sep 30 '22 02:09

unDeadHerbs