Publications
of Jon Jagger
jon@jaggersoft.com
Appeared in CVu 10.1, November 1997

{ yourself }
Macro Hunter

Here's something that's not uncommon:

void * 
debug_malloc(size_t n, 
             const char * file, 
             int line);

#define malloc(n)    \
    (debug_malloc((n), __FILE__, __LINE__))

The ISO-C Standard says that __LINE__ is:

“the line number of the current source line (a decimal constant).”
Some further digging in the standard reveals:
“The type of an integer constant is the first of the corresponding list in which its value can be represented. Unsuffixed decimal: int, long, unsigned long int;...”
It's possible that if __LINE__ is a very large number, its type may not be int. It could be signed long, or unsigned long. In the context of dbg_malloc, a solution to this is easy, just cast __LINE__ to an unsigned long:

void * 
debug_malloc(size_t n, 
             const char * file, 
             unsigned long line);

#define malloc(n)      \
    (debug_malloc(     \
        (n), __FILE__, (unsigned long)__LINE__))

And all will be well again. However, there is another solution, and I'd like to explore it a little, mainly because it highlights so many gotchas that lurk in the pre-processor.

The other solution is to convert __LINE__ into a const character string. In other words, if __LINE__ evaluates to 366 you would like to use "366" rather than (unsigned long)366. This actually makes some sense in the context of debug_malloc, since __FILE__ is already a const character string. Here's a first cut:

void * 
debug_malloc(size_t n, const char * details);

#define malloc(n)    \
    (debug_malloc(   \
        (n), __FILE__ ":" # __LINE__))

This is using two C features. First it uses the pre-processor # operator to stringize the __LINE__ argument. Second it uses the built in behaviour of the parser to concatenate three adjacent string literals. It doesn't work. If you try it, you'll find that you can't write #__LINE__ because __LINE__ is not the name of a macro function parameter. Only n is. However this is easy to solve, since you can pass __LINE__ as the parameter to a nested macro call:

#define STR(line)    #line

#define malloc(n)      \
    (debug_malloc(     \
        (n), __FILE__ ":" STR(__LINE__)))

But this doesn't work either! If you try this you'll find STR(__LINE__) evaluates to "__LINE__". What you have to write, { yourself }, is:

#define STR_1(line)   #line

#define STR_2(line)   STR_1(line)

#define malloc(n)      \
    (debug_malloc(       \
         (n), __FILE__ ":" STR_2(__LINE__)))

And this works. Subtle isn't it.

I was thinking about this double macro call the other day. I started to wonder whether it would be possible to write a macro function whose purpose was to test whether it's argument was itself a macro [1] An example will help.

#define IS_MACRO(tokens) boolean-expression

int feof_is_macro = IS_MACRO(feof(stdin));

The idea is that feof_is_macro will be assigned the value 1 (non-zero, true) if there is a macro for feof (no doubt something like this...)

#define feof(_stream) ((_stream)->_flag & _IOEOF)
and it will be assigned the value 0 (zero, false) if there isn't a macro for feof. It turns out that there is a way to create this macro hunting macro:

#define IS_MACRO(tokens)    \ 
    (different(#tokens, STR_1(tokens)))

int 
different(const char * raw, const char * cooked)
{
	return strcmp(raw, cooked) != 0;
}

This seems to work. And in fairness, it does. But as is often the case with all things related to the pre-processor, there is a nasty subtlety waiting to trip us up. The problem is in the call! feof may not have macro, but stdin might. For example:

#define stdin (&_iob[0])

In this case the raw version of feof(stdin) is feof(stdin) and the cooked version of feof(stdin) is feof((&_iob[0])). You might easily be fooled into thinking that feof has a macro when it hasn't. Subtle isn't it. Can you solve this problem? You can. Remember that no call of feof ever actually takes place. IS_MACRO is merely playing tricks to see what token sequence the C pre-processor would hand to the C compiler if feof was called. Subtle isn't it. The only requirement is that the call has the correct number of arguments. What is required is an argument that is guaranteed not to have a macro of its own. Hence:

int feof_is_macro = IS_MACRO(feof(1));

You can be sure there is no macro for 1 because that's illegal. Subtle isn't it.

Admittedly this is a slightly contrived example. But I hope it highlights that the pre-processor is a dangerous tool. Often it is too blunt; it does not respect scope. At other times it is too strange; it's behaviour is counter-intuitive.

That's all for now.
Cheers
Jon Jagger
jon@jaggersoft.com

[1] I'm like that. These strange ideas just pop into my head.