add Go-style defer mechanism for C++#249
Conversation
|
thanks for the pull request! that being said, I recognise that a functionality like this might be necessary, so please give a better example to convince me there is no better way. even if we have to accept a macro, the name is no good. macros should scream that they are bad (ALL_CAPS and a long inconvenient name, so it doesn't proliferate), and the name mustn't be at risk of being used anywhere else. finally, macros are unscoped, so we also have to prevent conflicts with other libs. so this would mean a name like ALP_DEFER_UNTIL_END_OF_SCOPE). |
|
I agree with most of your concerns about macros. I don't see macros as an ideal language feature either, and if C++ had a built-in I also agree that the examples I provided weren't the strongest. In particular, the The use case I'm trying to address is not resource ownership where RAII is clearly the right solution, but scope-exit actions that are local to a single scope. Typical examples are balancing API calls (begin.../end..., push.../pop...), temporarily modifying state that must be restored, or accumulating several independent cleanup actions while using multiple early returns. For example, in a WebGPU render function: Here the cleanup isn't about owning pass or encoder. Tt's about satisfying API protocol regardless of which of several exit paths is taken. The alternatives are either duplicating cleanup before every return, restructuring the function around a single exit, or introducing one-off RAII wrapper classes whose only purpose is to call I think this is the niche where defer is valuable: it complements RAII rather than replacing it. If the cleanup naturally belongs to a reusable type, RAII is still the better solution. If the cleanup is algorithm-specific and only meaningful within a single function, an inline scope-exit action is often the simpler expression. Regarding the macro name, I understand the motivation for making macros look "ugly" to discourage their use. However, in this case the macro isn't introducing a project-specific abstraction—it is emulating a language construct that already exists in several languages (Go, Zig, Jai, Odin, etc.). The intention is that when someone sees
they immediately recognize the semantics from those languages: execute this block when leaving the current scope. Giving it a long, project-specific name like In other words, the macro isn't trying to make scope-exit actions more ergonomic than existing C++ facilities. It's trying to express an already well-known concept using the spelling people expect. The implementation happens to require a macro because C++ lacks native syntax, but ideally users should think of it as "the missing defer keyword," not as "yet another project macro." I agree with moving the helper macros to the |
|
hi, as to the name, we could debate that for some time, but i'd like to cut it short:
you mentioned scope_exit, which also looks like a nice syntax to me. can't we just include offa/scope-guard? we did something similar with tl::expected here. What do you think about that? |
|
Hi, defer { ... };to: nucleus::scope_exit guard([&] { ... });This has the nice advantage that the implementation can later be swapped to the C++23 version without requiring any code changes. Only the namespace would need to change from I personally would prefer not to add a dependency just for this kind of small convenience feature. The implementation is simple enough to keep local, and avoiding an additional dependency keeps the project easier to maintain. |
|
yes, i like the scope_exit guard version much more. it's a bit more code, but it feels much more idiomatic. about including it via dependency or our own implementation: imo the pros and cons pretty much balance each other out. the library is certainly better tested, and people thought about failure cases and a optimal implementation. and there is unique_resource as well, but that's as easy to implement on our own. I do see your point about having an unnecessary dependency, it's still a git clone and cmake noise. + we don't have exceptions, which likely is the most problematic part. so yeah, either way works, really :) |
Introduce a lightweight
defermacro that executes a lambda when leavingscope, providing a simple way to express cleanup actions in C++.
The implementation uses RAII and relies on compiler inlining to generate code
close to hand-written cleanup paths.
Code generation comparison:
https://godbolt.org/z/PWzjY66Ga
Example:
The deferred action runs automatically at the end of the scope, including
when leaving through early returns.