5 ms·
I wonder if this can be emulated with for loop: for(FILE *fp = fopen(path, "wb"); fp != NULL; fclose(fp), fp = NULL) { ... }
by okla 5y ago
I wonder if this can be emulated with for loop:
for(FILE *fp = fopen(path, "wb");
fp != NULL;
fclose(fp), fp = NULL) {
...
}
- josephcsible 5y agoNo, because exiting the block early (e.g., with return) won't run the cleanup code.
- jstimpfle 5y agoThat's a little bit like saying, it's impossible to write functioning code because you could make a mistake. But an abstraction that can be misused might still be an abstraction worth using. Here is a macro that can be used to emulate a defer #define CONCAT_(a, b) a ## b #define CONCAT(a, b) CONCAT_(a, b) #define UNIQUENAME() CONCAT(i_, __LINE__) #define SCOPE_(counter, init_stmt, exit_stmt) for (int counter = ((init_stmt), 1); counter--; (exit_stmt)) #define SCOPE(init_stmt, exit_stmt) SCOPE_(UNIQUENAME(), (init_stmt), (exit_stmt)) Granted it's a hack, but it can be useful at times. I've used something like it to define a large data hierarchy in code for example, as having to close all the nodes manually is tedious. You could wrap a second for-loop around the definition of the macro to at least be able to catch misplaced "break" statements.
- okla 5y agoLooks very interesting. Can you please provide usage example?
- jstimpfle 5y agoSure, you'd code like SCOPE(Resource *ptr = acquire_resource(), release_resource(ptr)) { // do stuff with resource ptr. } Actually, to allow variable declarations in the init_stmt like above, you'll need to use two nested for-loops: #define SCOPE_(name, begin_stmt, end_stmt) for (int name = 0; !name; assert(name && "should never break from a SCOPE")) for (begin_stmt; !name; name++, (end_stmt)) It is natural to add another layer of specific usage macro like this: #define UI_NODE(ctx) SCOPE(push_ui_node(ctx), pop_ui_node(ctx)) UI_NODE(ctx) { ui_color(ctx, UI_COLOR_RED); const char *items[3] = { "a", "b", "c" }; for (int i = 0; i < 3; i++) { UI_NODE(ctx) ui_text(ctx, items[i]); } } Naturally, if you're already on C++, better code these macros in terms of RAII instead of abusing for-loops. That will add a little robustness.
- okla 5y agoI'm on C89, but this is a very useful trick. Thanks!
- taeber 5y agoI'm glad you suggested that, because one of the first comparisons I drew in my article[1] about the "with"-syntax was how it looks like a for-loop. If you are interested, I did provide a few macros[2], but as another commenter pointed out, they won't handle an early return. You can use "break" to exit early though: #define with(declare, startup, cleanup, block) \ { \ declare; \ if (( startup )) { \ do { \ block; \ } while(0); \ cleanup; \ } \ } with (FILE *fp, fp = fopen(path, "rb"), fclose(fp), { fread(buf, 1, sizeof(buf)-1, fp); if (ferror(fp)) { perror(path); break; } printf("%s\n", buf); success = 1; }) There's also "withif" #define withif(declare, startup, cleanup, block, otherwise) \ { \ declare; \ if (( startup )) { \ do { \ block; \ } while(0); \ cleanup; \ } otherwise; \ } [1]https://github.com/taeber/cwith https://github.com/taeber/cwith [2]https://github.com/taeber/cwith/blob/master/with.h https://github.com/taeber/cwith/blob/master/with.h