Free Software programmer
rusty@rustcorp.com.au
Subscribe
Subscribe to a syndicated
feed of my weblog, brought to you by the wonders of
RSS.
This blog existed before my current employment, and obviously
reflects my own opinions and not theirs.
This work is licensed under a Creative Commons Attribution 2.1 Australia License.
Categories of this blog:
IP issues
Technical issues
Personal issues
Restaurants
Older issues:
All 2008 posts
All 2007 posts
All 2006 posts
All 2005 posts
All 2004 posts
Older posts
|
Rusty's Bleeding Edge Page
Fri, 13 Mar 2009
So, C has a preprocessor, and it can be used for evil:
particularly function-style macros (#define func(arg)) are generally
considered suspect. Old-timers used to insist all macros were
SHOUTED, but it can make innocent (but macro-heavy) code damn ugly.
Remember, it's only a problem when it's Easy to Misuse, and if you've written something
that's easy to misuse, maybe a rethink is better than an ALL CAPS warning.
There is one genuine and unescapable use for macros:
- Macros which deal with types, or take any type
- The classic here is
#define new(type) ((type *)malloc(sizeof(type))) But consider also the Linux kernel's min() implementation:
#define min(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })
which uses two GCC extensions to produce a warning if x and y are not
exactly the same type.
And there are several justifiable but more arguable cases:
- Const-correct wrappers
- If you need to wrap a struct member access, it's annoying to do
it as an inline function. To be general a function needs to
take a const pointer argument, then cast away the const (see
strchr). Const exists for a reason, and stealing it from your callers
is a bad idea. A macro
#define tsk_foo(tsk) ((tsk)->foo) maintains const correctness, at
the slight cost of type safety (you could hand anything with a
foo member there, though it's unlikely to cause problems and
can be fixed with a more complex macro.
- Debugging macros
- Generally just add __FILE__ and __LINE__ to a function call. The
non-debug versions are generally real functions.
- Genuinely fancy tricks
- There's no good way around a macro for things like ARRAY_SIZE and BUILD_BUG_ON (these taken from CCAN):
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + _array_size_chk(arr))
#define BUILD_ASSERT(cond) do { (void) sizeof(char [1 - 2*!(cond)]); } while(0)
More questionable still:
- Macros which declare things
- This is for things which need initialization, eg. LIST_HEAD
in the kernel (or ccan/list) expects an "empty" list to be pointing
to itself. While this is convenient, nothing else in C
self-initializes so it's arguably better to provide an
"EMPTY_LIST(name)" macro. You get a nice crash if you forget
(except on stack vars).
- Macros which iterate
- list_for_each() (ccan/list.h version of the kernel's list_for_each_entry):
#define list_for_each(h, i, member) \
for (i = container_of_var(debug_list(h)->n.next, i, member); \
&i->member != &(h)->n; \
i = container_of_var(i->member.next, i, member))
It's less explicit, but much shorter than having three macros and using
them to loop:
for (i = list_start(&list, member); i != list_end(&list, member); i = list_next(&list, member, i))
If we sacrifice a little efficiency for convenience, we can make list_start()
and list_next() evaluate to NULL at the end of the list, and I prefer it over
the list_for_each() macro:
for (i = list_start(&list, member); i; i = list_next(&list, member, i))
Of course, it wouldn't be a complete post on macros without mentioning
things you should never do:
- Modify your arguments.
- C coders don't expect magic changes to parameters. From kernel.h:
#define swap(a, b) \
do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
- Embed control statements to places outside the macro.
- Putting 'return' in macros is only ok if the macro is called, say,
COMPLAIN_AND_RETURN. And then it's probably still a bad idea.
- The classics: use too few brackets, or allow multi-evaluation.
- The former is unforgivable; it cost be 1/2 a day of my life once
when I was younger and using another coder's RAD2DEG() macro.
The latter can be avoided with gcc extensions (see min() above), or
sometimes using sizeof().
[/tech] permanent link
|
|