Journal Articles

CVu Journal Vol 1, #3 - Feb 1988 + Programming Topics
Browse in : All > Journals > CVu > 013 (15)
All > Topics > Programming (877)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: Debugging with the Macro Processor

Author: Martin Moene

Date: 28 June 2010 08:46:00 +01:00 or Mon, 28 June 2010 08:46:00 +01:00

Summary: 

Body: 

The C macro processor is the first pass of the compiler that converts #define symbols into the data in their definitions. An example of this is:

#define TRUE 1
#define FALSE 0

main() {
if(TRUE)
   printf("TRUE");
else
if(FALSE)
   printf("FALSE");
}

This code is just a bit of nonsense to show how the macro processor works. Here is how it would look after the macro expansion pass of the C compiler:

main()
{
   if(1)
      printf("TRUE");
   else
   if(0)
      printf("FALSE");
}

Notice that TRUE has been replaced by 1 and FALSE has been replaced by 0 (a zero value is 'false' to C). The action of this program is to print the word TRUE. Notice that the strings "TRUE" and "FALSE" have been preserved. Macro processing is not just a textual substitution such as would be done with an editor.

An important feature of the macro processor is the ability to test the value of a defined sybmol and alter how code is compiled accordingly. The most common use of this is to selectively include extra code for debugging purposes.

#define DEBUG
.......
#ifdef DEBUG
   printf("some debugging message....");
#endif

If you wish to remove the debugging printf from the program then the only thing that needs to be removed is the "#define DEBUG" as if the symbol "DEBUG" is not defined the first pass of the compiler will omit everything between an #ifdef DEBUG and its matching endif. For only one message this is little different from the work involved in placing a comment arount the printf statement to get the same result but if the program has many debugging messages instead of one then it is much quicker to control them with the macro processor.

With some compilers it is possible to supply definitions to the macro processor on the command line so the #define DEBUG need not be present in the file itself.

Unlike comments ifdef/endif pairs can be nested, each endif will match the most recent ifdef. If there are many ifdef/endif pairs in the code it is a good idea to 'tag' each endif with the ifdef it belongs to to save confusion - like this:

#ifdef DEBUG
   printf("some debugging message....");
#endif /* DEBUG */

The comments around the second DEBUG may not be need for all compilers but

Microsoft C for one complains about extra characters after an endif directive. As comments are the first things to be stripped out by the macro processor a comment can be put anywhere without changing the sense of anything.

What I have just presented above is the 'traditional' way to include debugging code. If more use is made of macro processor features debugging can be made easier and neater.

The most powerful feature of the macro processor is that macros are allowed to have paramerets. The best way to explain this is with an example:

#define MAC(A, B, C) (A = B * C)

main() { int v1, v2, v3; v2 = v3 = 10; MAC(v1, v2, v3); }

will exand to:

main()
{
   int v1, v2, v3;
   v2 = v3 = 10;
   v1 = v2 * v3;
}

Although the use of macros with parameters looks like function calls the effect is to 'expand' into the code of the macro definition. The MAC macro above simply wouldn't work as a function. If v1 was a parameter it could not be assigned the value of v2 * v3.

The last tool we need to be aware of before building our debug tools is the 'special' macro names. There are two symbols that the macro processor defines for itself but are accesable inside other macro definitions. The macro __LINE__ (note the double underscores on each side of the definition) is the current line number that the macro processor has got to in the source file and __FILE__ is a string containing the name of the current file being processed. These are two things that are very useful to know in a debug message!

Here then is the first of my debug macros:

#ifdef DEBUG 
   #define DEBUG0(S) {printf("%s(%d) %s", __FILE__, __LINE__, S);}
#else
   #define DEBUG0(S)
#endif

Note that if DEBUG is not defined the DEBUG0() macro has no body. This means that any DEBUG0 in the code will dissappear if DEBUG is not defined. Instead of having to brace the printf with #ifdef DEBUG/#endif in the code every time it is used the DEBUG0 can be used directly in place of the printf.

Here we come across a difference between macros and functions that requires some thought. The printf function can take a variable number of arguments but the above macro can only take one argument so it is only any good for printing constant strings. The only way around this is to have a range of DEBUG macros that accept different numbers of arguments. Here is one that will accept a printf format string and two variable items to print:

DEBUG3(S, X, Y) {printf("%s(%d) ",__FILE__, __LINE__); \ 
   printf(S, X, Y); }

A good convention is to end the name of the macro with the number of argumets it expects.

Another common way of debugging is to put an if statement around the printf so that the message is only printed if a condition is true. This can also be catered for neatly as a simple macro - just a variant of the DEBUG macro set discussed above. Here is a the DEBUG3 macro with an added if part:

IFDEBUG3(C, S, X, Y) if(C){printf("%s(%d) ",__FILE__, __LINE__); \
   printf(S, X, Y); }

The message is only printed if condition C is true. C can be anything that would be legal in an if statement.

IFDEBUUG3(x > 10 && y > 20, "x is %d and y is %d\n", x, y);

Sensible use of macros can do much to make C programming easier to do and C programs easier to read. If debugs only take one line instead of 3 or more then they detract much less from the readability of the underlying program.

Notes: 

More fields may be available via dynamicdata ..