Skip to content

Match

Description

Open a type match over the static type of x. The scrutinee is copied by value (so rvalues / function returns work) and exposed to the arms as it. Pair with When arms and an optional trailing Otherwise. Arms are mutually exclusive; the block runs at most one of them.

Because the selector is a _Generic constant, exactly one arm is live per instantiation and the construct folds to it; the others are dead but must still compile against it’s static type (it has one concrete type here). This makes Match meaningful inside generic/macro code, and free everywhere else.

Usage example (from documentation)

  Match(value) {
      When(int)    WriteFmtLn("int {}", it);
      When(double) WriteFmtLn("double {}", it);
      Otherwise    WriteFmtLn("other");
  }

Success

Runs the body of the single arm whose type matches x (or Otherwise if none), with it bound to x. Block exits after one arm.

Failure

If no When matches and there is no Otherwise, the match is non-exhaustive and aborts via LOG_FATAL – it never silently falls through. A When body that misuses it’s static type is a compile error (every arm is type-checked).

Usage example (Cross-references)

Usage examples (Cross-references)
        Number n   = Number_from_int(42);
        bool   hit = false;
        Match(n) {
            When(Number, int) hit    = (it == 42);
            When(Number, double) hit = false;
        Number m  = Number_from_double(2.5);
        bool   hd = false;
        Match(m) {
            When(Number, int) hd    = false;
            When(Number, double) hd = (it == 2.5);
        int    ai = 0;
        double bd = 0.0;
        Match(a) {
            When(Number, int) ai    = it * 2; // it : int
            When(Number, double) bd = it;
            When(Number, double) bd = it;
        }
        Match(b) {
            When(Number, int) ai    = it;
            When(Number, double) bd = it * 2.0; // it : double
    // value in, value out -- no pointers
    static Number twice(Number n) {
        Match(n) {
            When(Number, int) return Number_from_int(it * 2);
            When(Number, double) return Number_from_double(it * 2.0);
        Number r  = twice(Number_from_int(21));
        bool   ok = false;
        Match(r) {
            When(Number, int) ok    = (it == 42);
            When(Number, double) ok = false;
        bool ok = false;
        // Match directly on a function-return rvalue; float-less Number -> Otherwise unused.
        Match(twice(Number_from_double(1.5))) {
            When(Number, int) ok    = false;
            When(Number, double) ok = (it == 3.0);
        Cell c  = Cell_from_char('Z');
        bool ok = false;
        Match(c) {
            When(Cell, int) ok  = false;
            When(Cell, char) ok = (it == 'Z');
        bool ok     = true;
        for (int k = 0; k < 3; k++) {
            Match(vals[k]) {
                When(Tri, int) ok = ok && (it == 7), seen |= 1;
                When(Tri, float) ok = ok && (it == 1.5f), seen |= 2;
        Number n     = Number_from_int(7);
        int    count = 0;
        Match(n) {
            When(Number, int) count++;
            When(Number, double) count++;
    bool deadend_variant_nonexhaustive(void) {
        Number n = Number_from_double(9.0); // holds double, only int handled, no Otherwise
        Match(n) {
            When(Number, int)(void) it;
        }
    
        int tag = -1;
        Match(i) {
            When(int) tag    = 0;
            When(double) tag = 1;
        bool ok = tag == 0;
    
        Match(d) {
            When(int) tag    = 0;
            When(double) tag = 1;
    
        // Unlisted type falls to Otherwise.
        Match(p) {
            When(int) tag    = 0;
            When(double) tag = 1;
        int    ri = 0;
        double rd = 0.0;
        Match(i) {
            When(int) ri    = it * 2;
            When(double) rd = it;
            When(double) rd = it;
        }
        Match(d) {
            When(int) ri    = it;
            When(double) rd = it * 2.0;
        bool hit = false;
        // Scrutinee is an rvalue; copied by value into `it`.
        Match(i + 1) {
            When(int) hit    = (it == 5);
            When(double) hit = false;
        int i     = 7;
        int count = 0;
        Match(i) {
            When(int) count++;
            When(double) count++;
        Position2D p   = {1.0f, 2.0f};
        bool       hit = false;
        Match(p) {
            When(int) hit    = false;
            When(double) hit = false;
        int    oi = 0;
        double id = 0.0;
        Match(i) {
            When(int) {
                Match(d) {
        Match(i) {
            When(int) {
                Match(d) {
                    When(int) id    = -1.0;
                    When(double) id = it; // inner `it` : double
    bool deadend_match_nonexhaustive(void) {
        Position2D p = {1.0f, 2.0f};
        Match(p) {
            When(int)(void) it; // never matches Position2D, and there is no Otherwise
        }
    #define Match(x)                                                                                                       \
        for (bool MisraMatched = false, UNPL(tm_once) = true; UNPL(tm_once); UNPL(tm_once) = false,                        \
                  ASSERT_OR_FATAL(MisraMatched, "Match: no arm matched and no Otherwise (non-exhaustive)"))                \
            for (TYPE_OF(x) MisraSubject = (x), *UNPL(tm_loop) = &MisraSubject; UNPL(tm_loop); UNPL(tm_loop) = NULL)
Last updated on