 |
 |
Obscure C-preprocessor macro expansion question
|
 |
|
 |
|
Mac Enthusiast
Join Date: Jan 2001
Status:
Offline
|
|
I'm attempting to do some funky things with the C preprocessor, and could not find any useful references on how macro arguments are evaluated. Specifically, my problem can be boiled down to the following:
Say I define a macro that will generate a 'for' loop.
#define MAKE_A_LOOP_BAD(TIMES, BODY) do { \
int i; \
int times = (TIMES); \
for(i = 0; i < times; ++i) BODY \
} while(0)
I can use it like so:
MAKE_A_LOOP_BAD(10, { printf("hello\n"); }); // this is OK
However, if I have a less-trivial loop body, say something like:
MAKE_A_LOOP_BAD(10, { int a, b, c; printf("hello\n"); }); // this breaks -- note the commas between a, b, c
... the macro breaks down completely; note the commas between the variable declarations. Each comma seems to trigger a new argument to the macro.
Interestingly, the following seems to work fine:
MAKE_A_LOOP_BAD(10, { printf("number = %d", number); }); // why is this OK too? there is a comma in the printf call, the macro sees these as new arguments
... even though there is a comma in the printf call. I assume this is because the comma is wrapped with parenthesis.
Now, I seem to be able to fix the issue with a variable-argument macro like so:
#define MAKE_A_LOOP_GOOD(TIMES, BODY, ...) do { \
int i; \
int times = (TIMES); \
for(i = 0; i < times; ++i) BODY , ## __VA_ARGS__ \
} while(0)
and all the examples I have tried seem to work:
MAKE_A_LOOP_GOOD(10, { printf("hello\n"); });
MAKE_A_LOOP_GOOD(10, { printf("number = %d", number); });
MAKE_A_LOOP_GOOD(10, { int a, b, c; printf("hello\n"); });
Now, I don't mind using this workaround macro, but I need to understand why the former one doesn't work. I would like to see the official spec that says "commas within parenthesis aren't considered macro argument delimiters, but commas wrapped in curly-braces are." Otherwise I feel like a bunch of my code is going to be resting on a C-preprocessor quirk that probably shouldn't work at all.
Anybody dealt with this before? Or know where I could find such a spec?
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Feb 2000
Location: Nashua NH, USA
Status:
Offline
|
|
Macros bad. Don't use them. Its harder to figure out what the macro does than write the for loop. Your just making it unnecessarily complex.
|
|
|
| |
|
|
|
 |
|
 |
|
Clinically Insane
Join Date: Oct 2001
Location: San Diego, CA, USA
Status:
Offline
|
|
I really wish somebody would make a decent preprocessor for C. It doesn't have to be Lisp, but the C preprocessor is pretty all-around bad. I doubt many people actually understand everything it does.
|
|
Chuck
___
"Instead of either 'multi-talented' or 'multitalented' use 'bisexual'."
|
| |
|
|
|
 |
|
 |
|
Mac Enthusiast
Join Date: Jan 2001
Status:
Offline
|
|
The above is a *very* contrived example. I'm trying to find some info on how the preprocessor is supposed to work... What I'm actually doing does necessitate the use of macros to some degree, and generates some very complex code (e.g. not worth writing out more than once).
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Enthusiast
Join Date: Jan 2001
Status:
Offline
|
|
Yep, the preprocessor can be rather lame. M4 would have been awesome, but it's just so ugly.
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Enthusiast
Join Date: Jan 2001
Status:
Offline
|
|
So apparently an independent cpp implementation ( http://www.cs.york.ac.uk/fp/cpphs/) exhibits the same behavior (commas within parenthesis are NOT argument delimiters). Chuckit, the link above could be a semi decent solution if you are looking for a slightly "cleaner" preprocessor (though it's not quite as full featured). For example, you can write fully recursive expansions if you really wanted to. Of course this comes with the minor hassle of having to get a haskell runtime on your system, grrr.
But in any case, given both cpp's I've tried exhibit the same behavior, I guess I'll rely on it blindly for now.
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Feb 2000
Location: Nashua NH, USA
Status:
Offline
|
|
If the code is so complex that you don't want to write it multiple times, write a function. Thats what they were created for. If the only variation between these complex loop is the code sitting in the "body" use a function pointer to separate the loop from the body. Then all you have to do is write the body functions and reuse the loop.
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Enthusiast
Join Date: Jan 2001
Status:
Offline
|
|
I do have a function version of this particular hunk of code. I would have liked a simple macro version for the sections where I can't afford the performance impact of multiple function calls (especially those indirected through a pointer 'n' times where 'n' could be upwards of 100,000... and this is called potentially many times per second).
This was purely an aesthetic experiment... I could make this work 100% by structuring the macro with seperate BEGIN / END calls, but I find that so friggin' ugly.
|
|
|
| |
|
|
|
 |
|
 |
|
Clinically Insane
Join Date: Oct 2001
Location: San Diego, CA, USA
Status:
Offline
|
|
EDIT: Misunderstood. Sorry.
|
|
Chuck
___
"Instead of either 'multi-talented' or 'multitalented' use 'bisexual'."
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Feb 2000
Location: Nashua NH, USA
Status:
Offline
|
|
I would have liked a simple macro version for the sections where I can't afford the performance impact of multiple function calls
lookup the keyword "inline"
There is nothing aesthetic about C preprocessor macros.
|
|
|
| |
|
|
|
 |
|
 |
|
Clinically Insane
Join Date: Oct 2001
Location: San Diego, CA, USA
Status:
Offline
|
|
You can't pass inline functions as arguments to other functions.
|
|
Chuck
___
"Instead of either 'multi-talented' or 'multitalented' use 'bisexual'."
|
| |
|
|
|
 |
|
 |
|
Mac Enthusiast
Join Date: Jan 2001
Status:
Offline
|
|
I thought I'd share some interesting things that I discovered in researching this.
http://www.unmutual.info/software/scexp (page seems down... see link below)
This is a really crazy project that exposes C syntax as s-expressions. It's basically a c-code generator, but you could theoretically do some really cool stuff with the macro capabilities.
See: sexpc - s-expression to C translation
Chuckit, your alternative preprocessor idea would be really cool... as for it "not having to be lisp"... I say "why not?".
In my spare time I'm going to explore an s-expression based "PRE-pre-processor", potentially based on this nifty little library: sfsexp home page . I'll post back if anything interesting comes of it.
There is another alternative preprocessor implementation ( mcpp -- a portable C preprocessor with Validation Suite). This exhibits the same behavior as cpp, but lacks variadic macros (though there is a dirty workaround). I think the determination here is that the macro expander is inextricably tied to C syntax, for better or for worse.
BLAZE, macros have their place.
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Feb 2000
Location: Nashua NH, USA
Status:
Offline
|
|
and typedef negates 90% of them.
For example I used the C preprocessor to #include a C# file cleanly so the string constants could be shared between C++ and C# projects.
A if your loop is that complex then the loop itself is more expensive than the function calls.
You should look at the loop to see if you can simplify it.
If your that crazy about not using function calls why don't you just use gotos?
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Enthusiast
Join Date: Jan 2001
Status:
Offline
|
|
Yeah, well thanks for all the extremely pertinent suggestions for my original question. Next time I feel like debating coding style I'll let you know.
I sometimes make extensive use of goto's, but they don't help in the this case. Please re-read the original post.
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Feb 2000
Location: Nashua NH, USA
Status:
Offline
|
|
How about this. We don't know because no one has bothered to try it.
|
|
|
| |
|
|
|
 |
|
 |
|
Posting Junkie
Join Date: Dec 2000
Status:
Offline
|
|
Originally Posted by Chuckit
You can't pass inline functions as arguments to other functions.
Huh? Yes you can.
|
|
|
| |
|
|
|
 |
|
 |
|
Clinically Insane
Join Date: Oct 2001
Location: San Diego, CA, USA
Status:
Offline
|
|
No. If it exists as an actual function that you can get a pointer to, it's not inline.
|
|
Chuck
___
"Instead of either 'multi-talented' or 'multitalented' use 'bisexual'."
|
| |
|
|
|
 |
|
 |
|
Fresh-Faced Recruit
Join Date: Jul 2009
Status:
Offline
|
|
I just found a solution how to "protect" commas in macro parameters. Just define another macro:
#define CONCAT(...) __VA_ARGS__
This will concatenate all actual parameters with commas between them. For example:
printf("%s %s!\n", CONCAT("hello", "world") );
It also works in macro parameters, i.e. the result of CONCAT is not reparsed:
#define ID(x) x
//...
printf("%s %s!\n", ID(CONCAT("hello", "world")) );
More on my website...
Hope this helps...
Ingo
(Last edited by ingomueller.net; Jul 14, 2009 at 07:36 AM.
(Reason:Need to promote my website ;-)))
|
|
|
| |
|
|
|
 |
 |
|
 |
|
|
|
|
|

|
|
 |
Forum Rules
|
 |
 |
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
|
HTML code is Off
|
|
|
|
|
|
 |
 |
 |
 |
|
 |
|