4 ms·
Tips on writing C macros
- paulbaumgart 17y agoWhat's that nonsense with the do-while loop? Why not just enclose the macro body in both curly braces and then in parentheses? Also, more useful (GCC only, though) than just enclosing parameters in parentheses, because it makes macros side-effect safe: http://en.wikipedia.org/wiki/C_preprocessor#Multiple_evaluation_of_side_effects http://en.wikipedia.org/wiki/C_preprocessor#Multiple_evaluat... Of course, in the macro in the article, the first 2 parameters need to be modifiable lvals anyway, so for those the whole deal with parentheses and the typeof construct is a moot point. I'd write the proposed macro as: #define MYMACRO(a,b,c) \ ({ \ typeof (c) _c = (c); \ a = b + _c; \ b = _c * 2; \ })
- deleted 17y ago[deleted]
- paulbaumgart 17y agoSame with what I suggested: #include <stdio.h> #define MYMACRO(a,b,c) \ ({ \ typeof (c) _c = (c); \ a = b + _c; \ b = _c * 2; \ }) int main() { int a = 0, b = 0; if (1) MYMACRO(a,b,1); else MYMACRO(a,b,2); printf("%d, %d\n", a, b); } ----- $ gcc -o test test.c $ ./test 1, 2 Edit: Just for context: this was responding to a post pointing out that the do-while construct allows you to use a semi-colon after the macro invocation.
- doug11235 17y agoAnd another feature I like about ({ }) macros (anyone know the proper name here?) is you can return values. Clearly more useful in an example a bit more complex but... #define ADD_ONE(a) ({ a + 1; }) x = ADD_ONE(2);
- tedunangst 17y agoThey are called statement expressions.
- greyboy 17y agoExcuse my lack of C knowledge, but using your proposed macro, what would be the result of calling: MYMACRO(x,y,x+y) with your inclusion of typeof()?
- paulbaumgart 17y agotypeof is pretty smart. Since in C all types are known at compile-time, the compiler can put in there in whatever type typeof's parameter resolves to- even if it's an expression. As an example: int main () { typeof (1.0f + 10) a; int * b = &a; } ----- $ g++ test.c test.c: In function ‘int main()’: test.c:3: error: cannot convert ‘float*’ to ‘int*’ in initialization I'm using g++ because it actually spits out type information for incompatible pointers- probably something having to do with it being an error in C++ but valid in C (with a warning, at least in GCC).
- michaelfairley 17y agoWith your code, placing a semicolon after the macro leads to a compile error. Semicolons are not valid after arbitrary blocks, but it is acceptable to place them after do-while loops. It's much nicer to be able to call "MYMACRO(1, 2, 3);" than it is to have to remember to leave the semi-colon off.
- paulbaumgart 17y agoIt compiles fine on GCC. For other compilers, the Wikipedia page I linked offers the caveat: "This construct is not legal ANSI C; both the typeof keyword, and the construct of placing a compound statement within parentheses, are non-standard extensions implemented in the popular GNU C compiler (GCC)."
- tb 17y agoThe do-while "nonsense" is a pretty standard construct, so much so that I'd never seen your approach - which I admit looks nicer. How well is that supported across different compilers? Is it part of C99, for example?
- paulbaumgart 17y agoNo, it's not in C99: http://www.redhatrenewals.com/docs/manuals/enterprise/RHEL-4-Manual/gcc/c-extensions.html http://www.redhatrenewals.com/docs/manuals/enterprise/RHEL-4... Admittedly I have little experience with compilers other than GCC, but all things considered, I'd shy away from using anything but really basic macros without the compound-statement-in-parens and typeof() extensions that GCC provides. Perhaps I spoke too harshly, calling it "nonsense". :-)
- paulbaumgart 17y agoSo, just to follow up, I saw that the OCUnit macros ( http://www.sente.ch/software/ocunit/ http://www.sente.ch/software/ocunit/ ) are all using defined using the do...while(0) guard. I was wrong. It's the more platform independent way to do things. For code that's only ever going to be compiled with GCC, though, the statement expression method is cleaner, I think.
- tedunangst 17y agoIt's only supported by gcc and some edg based compilers. edg has trouble with it in some c++ code. Just think of how adding this complicates your grammar.
- gchpaco 17y agoCurly braces and then parens is a GCC extension. If you're going to do complicated macrology, GCC is the way to go because of typeof and a bunch of related extensions designed to make macro use work. But it's still a staggering amount of effort and not worth it in the long run.
- abecedarius 17y agoThis ({ ... }) syntax was not in the original ANSI C or K&R. Is it in C99 or in GNU C only? I've only ever seen it in GNU C.
- sketerpot 17y agoWhat if you already have a variable called _c and you pass it to MYMACRO? Is there any way of generating a guaranteed-unique symbol, similar to GENSYM in lisp?
- yvueywa 17y agoTip 1, don't Tip 2, see tip 1
- neilc 17y agoThat is silly; there are plenty of situations in which macros are useful in C. I'd be inclined to avoid complex multi-line macros that multiply-evaluate their arguments unless there's a compelling need for them, however.
- _sh 17y agoHow do you step through multi-line macros in a debugger?
- shadytrees 17y agoGDB has some bare-essentials macro support for the preprocessor macros, and I imagine people can get fancier with their own GDB scripts and functions. http://sourceware.org/gdb/current/onlinedocs/gdb_11.html http://sourceware.org/gdb/current/onlinedocs/gdb_11.html
- ori_b 17y agoUse inline functions instead of macros whenever possible. They don't have all the tricky gotchas, and they generally have no performance impact over macros.
- dspeyer 17y ago> Passing MyArray[x+3] as a macro argument would lexically copy this expression in multiple locations in the macro expansion, causing the generated code to needlessly evaluate *(Myarray+x+3) again and again and again. Wouldn't any decent optimizing compiler eliminate those common subexpressions? That's one of the basics, isn't it? Of course, if you're in a tight enough loop to worry about that, check the assembly by hand.