Comment by kaba0

3 years ago

C has beyond useless “macros”, they should not be compared with Rust’s, that are actually useful.

C's macros are primitive and unsafe but by no means useless. Here's a somewhat silly example from embedded programming. I wanted to embed the bitmaps of a small set of characters for use on a bitmapped monochrome display. It was easy to define macros CHAR_GRID, _ and X such that e.g.

    const uint8_t zero[] = {
      CHAR_GRID(
        _,X,X,X,_,
        X,_,_,_,X,
        X,_,_,X,X,
        X,_,X,_,X,
        X,X,_,_,X,
        X,_,_,_,X,
        _,X,X,X,_
      )
    };

desugared to a column-major array of 5 bytes.

    #define CHAR_GRID(c1r1, c2r1, c3r1, c4r1, c5r1, \
    c1r2, c2r2, c3r2, c4r2, c5r2, c1r3, c2r3, c3r3, c4r3, c5r3, c1r4, c2r4, c3r4, c4r4, c5r4, c1r5, c2r5, c3r5, c4r5, c5r5, c1r6, c2r6, c3r6, c4r6, c5r6, c1r7, c2r7, c3r7, c4r7, c5r7) \
 c1r1 | (c1r2 << 1) | (c1r3 << 2) | (c1r4 << 3) | (c1r5 << 4) | (c1r6 << 5) | (c1r7 << 6), \
 c2r1 | (c2r2 << 1) | (c2r3 << 2) | (c2r4 << 3) | (c2r5 << 4) | (c2r6 << 5) | (c2r7 << 6), \
 c3r1 | (c3r2 << 1) | (c3r3 << 2) | (c3r4 << 3) | (c3r5 << 4) | (c3r6 << 5) | (c3r7 << 6), \
 c4r1 | (c4r2 << 1) | (c4r3 << 2) | (c4r4 << 3) | (c4r5 << 4) | (c4r6 << 5) | (c4r7 << 6), \
 c5r1 | (c5r2 << 1) | (c5r3 << 2) | (c5r4 << 3) | (c5r5 << 4) | (c5r6 << 5) | (c5r7 << 6)

While that’s true, preprocessors are pretty trivial to write these days if you want macros that the language doesn’t support. Racket excels at this.

  • Which preprocessor do you recommend for writing C?

    • None.

      Use an actual different language. Ada, Rust, Zig, D, Lisp/Scheme/Racket, Tcl, Forth, etc. ... something other than C.

      Don't preprocess C into a slightly broken other language that you wish it were. Use C as C, or use something else.