Warning! This site is under development.
Approximate overview
#define MAC(CROW) fprintf (stderr, "%s %s\n", CROW, CROW)MAC("Sim, uh");fprintf(stderr, "%s %s\n", "Sim, uh", "Sim, uh");#define SQUARE(X) ((X)*(X))#define DUMB_ZERO(X) ((X)-(X))
printf ("DUMB_ZERO(val++) = %d\n", DUMB_ZERO(val++));
Reminder; The first step of compilation. Primarily does easy textual transformations.
The big four
#include (brings in other files, typically .h files, but also .c files)#ifdef … #else … #endif (conditionally includes code in the
file)
#define (for both constants and macros)Bad example of architecture
#ifdef MC68000
#define intbits 8
#define shortbits 4
#endif
#ifdef x86
#define intbits 16
#define shortbits 8
#endif
#ifndef intbits
#define intbits 2
#define shortbits 1
#endif
Bad example of debugging
#ifdef DEBUG
fprintf (stderr, "Got here!\n");
#endif
A common fourth
#if … #elif … #else … #endifAnd beyond
_Pragma (formerly #pragma)#line#ident)What else did you learn from skimming?
Historical solution
#ifndef __FILE_H__
#define __FILE_H__
// CODE
#endif __FILE_H__
Hey, it’s better than stringizing.
#PARAM converts a parameter into a string.#define VERIFY(EXP) if (! (EXP)) fprintf (stderr, "Failed to verify that: %s\n", #EXP)VERIFY(x > 0)if (! (x > 0)) fprintf (stderr, "Failed to verify that: %s\n", "x > 0")I don’t know about you, but I’m uncomfortable with the lack of braces around the body.
#define VERIFY(EXP) if (! (EXP)) { fprintf (stderr, "Failed to verify that: %s\n", #EXP); }
Input
VERIFY(x > 0);
Output
if (! (x > 0)) { fprintf (stderr, "Failed to verify that: %s\n", "x > 0"); };
That makes us a bit uncomfortable.
Input
if (TODAY_IS_THURSDAY)
VERIFY(x > 0);
else
x = max(x, 1);
Output
if (TODAY_IS_THURSDAY)
if (! (x > 0)) fprintf (...);
else
x = max(x, 0);
Normal if model
if (TEST)
exp;
else
exp;
Note
if (TEST)
{ exp; };
// No else permitted
Wrap the code in a do { ... } while (0).
For example,
#define VERIFY(EXP) \
do { if (! (EXP)) { fprintf (stderr, " Failed to verify that %s\n", #EXP); } } while (0)
Now you can safely add a semicolon and use it in any situation.
Further improvements …
#define VERIFY(EXP) \
do \
{ \
if (! (EXP)) \
{ \
fprintf (stderr, " Failed to verify that %s on line %d of %s\n", \
#EXP, __LINE__, __FILE__); \
} \
} \
while (0)
Wasn’t that fun?
Note that you can play with __LINE__ and __FILE__ using #line
The ## operation connects the text of a macro parameter along with
some other text.
#define RECURSIVE_ARRAY_FUNC(NAME, TYPE, CODE) \
void NAME(TYPE array[], int n) { NAME ## _kernel (array, n, 0); } \
void NAME ## _kernel(TYPE array[], int n, int pos) { \
if (< pos n) { \
CODE; \
NAME ## _kernel(array, n, (+ pos 1)); \
} \
}
RECURSIVE_ARRAY_FUNC(increment, int, array[i] = array[i] + 1)
Isn’t metaprogramming fun?
Challenge: Write ARRAY_MAP(ARRAY, FUNC, N)
for (int i = 0; i < n; i++) { a[i] = FUNC(a[i]); }
E.g.,
ARRAY_MAP(ints, square, numints)
would give
for (int i = 0; i < numints; i++) { ints[i] = square(ints[i]); }
We don’t need the type, since a[i] = FUNC(a[i]) should work for
any type.
Or write ARRAY_BUILD(ARRAY, THUNK, N)
I.e.,
ARRAY_BUILD(ints, x++, numints)
would give
for (int i = 0; i < numints; i++) { ints[i] = x++; }
Bring your answers to the next class.