Multi-line macro syntax


A multi-line preprocessor directive, as provided in the MLM package, begins with the #begin directive and ends at the first line that starts with a #end directive that is not inside a comment or string. Both the #begin and #end directives may contain additional comments between tokens.

After the #begin, any valid cpp directive may appear, without its initial "#" character. However, the only useful multi-line preprocessor directive is a multi-line definition. For example, the following code defines a multi-line macro to make a statement that swaps two variables of type T.

#begin define swap(x,y,T)
do {
    T temp = x;
    x = y;
    y = temp;
} while (0)
#end /* swap */
A multi-line macro may contain lines that end in backslashes; the preprocessor will concatenate these lines to the following line, just as it does in ordinary macros. Only non-backslashed newlines are emitted as newlines in the final output. At each point in a macro expansion that corresponds to a newline in the macro definition, the preprocessor will emit a line directive. This directive allows the compiler to determine that its current source line is within the macro rather than within the source code that invoked the macro.

A longer example

This is an example of real code using the MLM extension to cpp. Note that multi-line macros can substitute very effectively for C++ template classes, which are actually just another form of multi-line macro. This code is from the Thor object-oriented database implementation.
#ifndef _SHORTP_H
#define _SHORTP_H

// 
// A "Shortp" is a slot-sized pointer used to reduce space usage, but it
// cannot be dereferenced directly. To
// dereference a short pointer, it must be first converted to a long
// pointer. It can also store bits of data (as many as the bits in a
// slot minus 1).
//
// A Shortp corresponding to the pointer type "Tp" is named
// "Tp_x". A "Tp_x" holding a pointer can be created from a "Tp", 
// "Tp_x::pointer" returns a "Tp". A "Tp_x" holding data can be created 
// from a Ubits32, "Tp_x::data" returns the contained data.
//

#include "utils/basic.h"
#include "utils/bits.h"
#include "common/slot.h"
#include "C/shortp.h"

//
// The following machine independent macro can be used to
// define a short pointer type for a long (64-bit) pointer type Tp
//

#begin define DEFINE_shortp(Tp)
class Tp##_x {
public:
    Tp##_x(void) {}
    /* Creates an uninitialized short pointer */

    Tp##_x(Tp tp) {x = (Shortp)tp;}
    /* Creates a short pointer holding a pointer */

    Tp##_x(Ubits32 d) {x = (Shortp)((d << 1)|1); }
    /* Creates a short pointer holding data. Only the bottom 31 bits
       of data are preserved. */

    bool is_data(void) { return (x & 0x1) ? TRUE : FALSE; }
    /* Returns true iff short pointer holds data */

    bool is_pointer(void) { return !is_data(); }
    /* Returns true iff short pointer holds a pointer */

    Tp pointer() {
    /* Requires short pointer holds a pointer */
      assert(!is_data());
      return (Tp)x;
    }

    Shortp data() {
    /* Requires the short pointer is holding a pointer */
      assert(is_data());
      return (x >> 1);
    }

private:
    Shortp x;
}
#end

#endif /* _SHORTP_H */

Andrew Myers