4 ms·
The best way to think about defer is that all the deferred statements are put in a goto label at the end of the block. When you break/return/whatever, it just j
by anonunivgrad 6y ago
The best way to think about defer is that all the deferred statements are put in a goto label at the end of the block. When you break/return/whatever, it just jumps to that label first. This just makes slightly more convenient what the standard approach to error handling in C systems programming already is.
Looking at the article, this specific implementation doesn’t quite go all the way, requiring a special guard {} block instead of working anywhere. A better implementation, working in any context, would be easy but has to be baked into the compiler.
Edit: just saw the proposal for inclusion into the C standard. It is unfortunate that they are considering 1) requiring a guard block and 2) deferring clean up to the end of the function instead of end of the scope. #1 is needless syntax and #2 would make the feature useless within loops by causing explosions and contractions in memory usage.
- ufo 6y agoReading the PDF, I got the impression that the main thing in favor of using a guard keyword is that it would allow it to be implemented as a library. That way it wouldn't require changes to compilers and it might also be possible to adopt into the C++ standard. However, the version without the guard keyword is arguably more ergonomic.
- derriz 6y agoHow can you get away from having an explicit guard block? Without one, you couldn't 'defer' to the end of a containing block from within an if/while/do/for/etc. block. When/where the deferred code is executed has to be specified some way so an explicit marker for the defer 'scope' is surely required without severely limiting the utility of the feature.
- anonunivgrad 6y agoYou would just follow the C variable scope rules I think. But I see now how this creates an issue if you want to put the defer, but not the acquisition of the resource it releases, within a control statement.
- ufo 6y agoStarting in page 16, there is a section called "Do we want a guard keyword?" that talks about this. They give an example showing that deferring to the end of the containing block is possible without the guard keyword, although it's a bit wonky, requiring to duplicate the "if". With guard keyword: guard { void *ptr = malloc(12); if (ptr) { defer free(ptr); // Use ptr } // Use ptr some more } // free ptr here Without guard keyword: { void *ptr = malloc(12); defer { if (ptr) free(ptr); } if (ptr) { // Use ptr } // Use ptr some more } // free ptr here
- ludocode 6y agoThis is a silly example of course because you don't have to check free() for null; calling free(NULL) is a no-op. In a more realistic case, say some object was handed off so it shouldn't be cleaned up anymore, you can just add a should_cleanup flag and check it in the defer block. There's really no need for guard. Their other example for guard, running defer in a loop, requires closures. This means it requires dynamic memory allocation and so the complexity goes through the roof.
- yvdriess 6y agoI agree. Dlang already has scope guards (Appendix C in the OP) https://dlang.org/spec/statement.html#scope-guard-statement https://dlang.org/spec/statement.html#scope-guard-statement, and does so at block scope. For some code it's a more elegant solution (keeps acquire and release code close together), without introducing the usual C++ RAII issues.