B.3 Macro Processors

In this section, we have a brief look at some real-life macro processors. The first one is a general-purpose macro handling facility, while the second one is for a specific language use.

B.3.1 Real-world: M4 Macro Processor

This macro processor is a general-purpose macro processor, used extensively on Unix and Unix-like systems, including Linux. An extended version is available as a GNU package.

M4 is a macro processor, in the sense that it copies its input to the output, expanding macros as it goes. Macros are either built-in or user-defined, and can take any number of arguments. Besides just doing macro expansion, M4 has built-in functions for including named files, running shell commands, doing integer arithmetic, manipulating text in various ways, performing recursion, etc. It can be used either as a front-end to a compiler or as a macro processor in its own right.

It is widely available on all UNIXes and has been standardized by POSIX. It is used by GNU Autoconf.

A very simple example M4 can be used for simple embedded text replacement. If M4 receives the input

define(AUTHOR, William Shakespeare) A Midsummer Night's Dream by AUTHOR then it outputs

A Midsummer Night's Dream by William Shakespeare

B.3.2 Real-world: Pre-processing (cpp)

The C pre-processor, often known as cpp, is a macro processor that is used automatically by the C compiler to transform the program before compilation.

It is called a macro processor because it allows you to define macros, which are brief abbreviations for longer constructs. These macros are expanded in its output. It also allows “inclusion” of other source files. Additionally, it provides conditional compilation. It also removes most of the comments.

Some examples

#define NDEBUG
#define SIZE 100
#define ARRAY SIZE*4
#define max(x,y) ((x)>(y))?(x):(y)
#include <stdio.h>
#include "myheader.h"
#ifdef LINUX
#endif

defines (macro)

#define BUFFER_SIZE 1024

later in the source:

buffer = (char *) malloc (BUFFER_SIZE);

Replaced by pre-processor:

buffer = (char *) malloc (1024);
#define NDEBUG

Only NDEBUG is “defined” but it has null (no) value.

#define BUFFSIZE 4*SIZE
#define SIZE 10

what happens? is it valid?

Defines with Arguments

#define power2(x) (1<<(x))

when invoked later in the source code with

a = power2(3);

is replaced by pre-processor:

a = (1<<(3));

and compiler replaces by a = 8; It is better to put the argument in () in the expansion (why?).

More than one formal argument can be used. The ( in the define should immediately follow the macro name

#define power2 (x) (1<<(x))      what happens?

Define ends with new-line

The expansion part of a “define” ends on the new-line character. How to circumvent this?

#define NUMBERS 1, \
                2, \
                3

Later in the source code, we have

int x[] = { NUMBERS };

It is replaced by:

int x[] = { 1, 2, 3 };

include Directive

#include <FILE>

This variant is used for system header files. It searches for a file named FILE in a standard list of system directories. You can prepend directories to this list with the –I option.

#include ″FILE″

This variant is used for header files of your own program. It searches for a file named FILE first in the directory containing the current file, then in the same directories used for FILE.

Inclusion can be nested, to any reasonable depth.

The Standard list for UNIX and UNIX-like systems

/usr/local/include
/usr/lib/gcc–lib/TARGET/VERSION/include
/usr/TARGET/include
/usr/include

TARGET refers to the processor and platform, e.g. on a typical machine it is i486-slackware-linux VERSION is the gcc version, e.g. 3.2.3.

Conditional Directive

A directive that instructs the pre-processor to select if a portion of code is included in the final token stream passed to the compiler. It can test arithmetic expressions, or whether a name is defined as a macro, or a mix using the special “defined” operator. Resembles in some ways an if statement in C, but: an if statement is tested during the execution of your program, a pre-processor conditional directive is tested when your program is compiled. Its purpose is to allow different code to be included in the program depending on the situation at the time of compilation.

Conditionals are:

#if	    test any (restricted) integer expression
#ifdef	    test if a macro is defined
#ifndef	    test if a macro is not defined
#endif	    end of the #if group
#else	    if the condition being tested is not true
#elif	    if condition not true, open a new if group
defined	    used in #if to check if a macro defined

Other Facilities in gcc Pre-processor

There are a number of other facilities in the pre-processor (cpp) of gcc, like:

Stringification – convert expressions to strings

Diagnostics – provide pre-processor level messages

Line control – provide line number info to other phases

Pragma – define compiler-specific information etc.

Please refer to cpp documentation by looking up the man-pages.

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.176.166