Last active
June 10, 2025 05:19
-
-
Save asd142513/ee494971fc93b46a4ed81935e2e4261a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Original post: | |
https://thephd.dev/c2y-the-defer-technical-specification-its-time-go-go-go | |
Inspired by for loop and Python’s `with` statement: | |
https://en.cppreference.com/w/c/language/for.html | |
for ( init-clause ; cond-expression ; iteration-expression ) loop-statement | |
https://docs.python.org/3/reference/compound_stmts.html#with | |
Syntax: | |
with ( init-clause ; cond-expression ; cleanup-expression ) context-statement | |
Comparison: `thephd.dev defer` vs `with` | |
1. Automatically creates a new scope. | |
2. Supports only expressions — no deferred statements. | |
3. `break` is allowed within the `with` block. | |
4. Nested `with` statements are not allowed within init-clause, cond-expression, or cleanup-expression. | |
However, the context-statement can include `with`, just like in loop bodies. | |
5. No mid-scope defers. | |
- In the original `defer`, deferred statements could appear anywhere in the scope. | |
- This is suboptimal, as developers must read the entire block to find all deferred actions. | |
- The `with` statement improves clarity by forcing all deferred behavior to be declared at the start of the block. | |
6. Unlike `defer`, which helps maintain a flat block structure `with` introduces an additional level of nesting. | |
This is even worse in common `malloc`–`init_object` patterns as it introduces two levels of additional nesting. | |
Comparison: `for` loop vs `with` block | |
1. Trivially, context-statement is executed only once; `continue` is not allowed. | |
2. Unlike a `for` loop's iteration-expression, the cleanup-expression is evaluated when exiting via `break` or `return`. | |
Jump to outside of block with `goto` skips the cleanup-expression. | |
*/ | |
#include <threads.h> | |
extern int do_sync_work(int id, mtx_t *m); | |
int main(void) | |
{ | |
with (mtx_t m = {}; mtx_init(&m, mtx_plain) == thrd_success; mtx_destroy(&m)) { | |
for (int i = 0; i < 12; ++i) { | |
with (; mtx_lock(&m) == thrd_success; mtx_unlock(&m)) { | |
if (do_sync_work(i, &m) == 0) { | |
return 1; | |
} | |
} | |
} | |
} | |
return 0; | |
} |
One critical feature that the
with
statement lacks, compared todefer
, is handling initialization failure. While awith-else
could be introduced to handle failures in the condition expression, the lack of a variable to store the evaluated value of the condition expression could pose a serious problem. This issue is shared withfor
loops, but it's more critical in this context because we are performing initialization rather than just checking a condition.
Possible solution:
mtx_t m = {};
with (int ret = mtx_init(&m, mtx_plain); ret == thrd_success; mtx_destroy(&m)) {
for (int i = 0; i < 12; ++i) {
with (; mtx_lock(&m) == thrd_success; mtx_unlock(&m)) {
if (do_sync_work(i, &m) == 0) {
return 1;
}
}
}
} else {
if (ret == thrd_error) {
// ...
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I strongly agree that
defer
is more foundational thanwith
. But that’s precisely the point.defer
has too high degree of freedom. While restricting programmer freedom might go against the philosophy of the C language, it’s still important to find a sweet spot — just as we havefor
andwhile
loops despite the existence ofgoto
. My concern is thatdefer
might end up being confined to strictly limited use cases, much likegoto
.Additionally,
defer
introduces a gap between initialization and cleanup. Carelessly inserted code might interfere with proper handling of thedefer
statement. In contrast,with
helps eliminate this gap.One critical feature that the
with
statement lacks, compared todefer
, is handling initialization failure. While awith-else
could be introduced to handle failures in the condition expression, the lack of a variable to store the evaluated value of the condition expression could pose a serious problem. This issue is shared withfor
loops, but it's more critical in this context because we are performing initialization rather than just checking a condition.The
with
statement isn't perfect either, but I believe it's important to constrain the excessive degree of freedom thatdefer
introduces.