Or... Traversing Macro Pasting Hell
Full disclaimer: This article used to be very different but was also complete tosh. The code I wrote based on its assumptions and misinformation was working fine for days and then broke around 5 minutes after posting the article when I discovered a new use case. This new article addresses that use case and the subsequent solution
Macro pasting
The C preprocessor has a handy (if not essential) operator for pasting 2 tokens together, ##. I use it all over the place in my OOOCode project to generate class and function names, etc using macros. It can be used like this...
#define PASTE(ARG1, ARG2) ARG1 ## ARG2 | |
PASTE(Hello, World)(); | |
/* expands to */ | |
HelloWorld(); |
Now consider the following...
#define PASTE(ARG1, ARG2) ARG1 ## ARG2 | |
#define HELLO Hello | |
PASTE(HELLO, World)(); | |
/* expands to */ | |
HELLOWorld(); |
In this second example I have passed a macro in as an argument to the PASTE macro and as a result it does not get expanded. In order to fix this it is necessary to add a level of indirection...
#define _PASTE(ARG1, ARG2) ARG1 ## ARG2 | |
#define PASTE(ARG1, ARG2) PASTE(ARG1, ARG2) | |
#define HELLO Hello | |
PASTE(HELLO, World)(); | |
/* expands to */ | |
HelloWorld(); |
As an aside, this same problem (and solution) occurs with the quoting operator, #, too.
Variadic macros and swallowing extra commas
Now it gets interesting as the pasting operator can also be used in variadic macros to swallow commas when no arguments are provided...
#define CALLONE(FUNCTION, ARGS...) FUNCTION(1 , ##ARGS) | |
CALLONE(myFunc); | |
/* expands to */ | |
myFunc(1); |
Handy, yeah? Well sort of. The problem is that this still exhibits the same problems as above when macros are used as arguments...
Macro pasting variadic hell
#define CALLONE(FUNCTION, ARGS...) FUNCTION(1 , ##ARGS) | |
CALLONE(add, CALLONE(addOne)); | |
/* expands to */ | |
add(1, CALLONE(addOne)); |
The above code does not compile as the second macro call does not get expanded. So I tried this...
#define _CALLONE(FUNCTION, ARGS...) FUNCTION(1 , ##ARGS) | |
#define CALLONE(FUNCTION, ARGS...) _CALLONE(FUNCTION, ARGS) | |
CALLONE(add, CALLONE(addOne)); | |
/* expands to */ | |
add(1, addOne(1, )); |
I'm not sure if this would work in other environments but the C Preprocessor that comes with the OpenTV IDE doesn't swallow the comma in this case.
This gave me a big problem. I can either support macros as arguments or zero length argument lists... but not both :(
Believe me I tried a great many more constructions involving the ## operator and various indirections but to no avail. It just wasn't happening. Eventually (it was quite long time that may even have involved praying as I was a long way into my OOOCode stuff and this was pretty key) I came across a different solution involving detecting empty argument lists. Doing this is not simple and definitely not something I want to get into here, but just know that in the following example the ISEMPTY macro expands to 1 if the argument list supplied is empty or 0 if not...
#define ISEMPTY(ARGS...) /* Too complicated to put here, see http://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ */ | |
#define _PASTE(ARG1, ARG2) ARG1 ## ARG2 | |
#define PASTE(ARG1, ARG2) PASTE(ARG1, ARG2) | |
#define CALLONE0(FUNCTION, ARGS...) FUNCTION(1, ARGS) | |
#define CALLONE1(FUNCTION, ARGS...) FUNCTION(1) | |
#define CALLONE(FUNCTION, ARGS...) PASTE(CALLONE,ISEMPTY(ARGS))(FUNCTION, ARGS) | |
CALLONE(add, CALLONE(addOne)); | |
/* expands to */ | |
add(1, addOne(1)); |
For the ISEMPTY macro stuff, special thanks have to go to Jens Gustedt and this article:
http://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
Hi Peter,
ReplyDeleteI have the impression that you could well profit from integrating P99 into your OO project. It already has:
- default arguments for functions
- type generic macros as a replacement for function overloading
- macro conditional expansion
- a initialization convention (AKA constructor) that allows for something like P99_NEW(toto, ... arguments)
- exceptions that throw int's
- threads
Jens
Hi Jens,
ReplyDeleteYou're probably right although i'll likely have to do some porting to get it to work. In particular I can't use unnamed variadic arguments like this:
#define MYMACRO(...) doSomething(__VA_ARGS__)
I would have to change such macros to:
#define MYMACRO(ARGS...) doSomething(ARGS)
Anyway, nice to hear from you - I really did appreciate the empty argument stuff :)