Last active
August 25, 2025 04:40
-
-
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I don't necessarily disagree that
with
and Python's stylings here are nicer to use (and restrict things to just expressions). But, there's a few flaws to this:with
, like @fdwr stated, the combination of all of these things is just really a rearranging of the more fundamental operations.defer
being low-level in this syntactic way is meant to mirror the existing practice (__attribute__((cleanup(...)))
,__try
/__finally
in MSVC, etc.); a lot of these don't have this level of structure, and that means it can't be directly replaced by thiswith
statement.else
-clause on thewith
shows the error in having a (gated)with
statement:with
statement or prevent entering it.with
so you can work with the error code means you've now negated one of the benefits ofwith
(the reduced scope and lifetime of an object). This same problem happens with__try
/__finally
in MSVC: https://thephd.dev/_vendor/future_cxx/papers/C%20-%20Improved%20__attribute__((cleanup))%20Through%20defer.html#intro-try.finallyIt's for that reason that I didn't pursue a Python-
with
or C#-using
feature. I do think you can have this sort of feature to make working with deferred action easier, but I would prefer to start with the fundamental piece and have a general-purpose, no-strings-attached undo mechanism and then work up the ladder.