One big flaw in Go-style `defer` is that error handling and scoping are an afterthought, and it's disappointing to see this is not improved upon in this proposal (at least as I understand the proposal). I do think this matters in practice. If I have a `func whatever() error` IME it's a common to accidentally do something like `defer whatever()` without catching handling the error. To work around that you'd need to do…
> One big flaw in Go-style `defer` is that error handling and scoping are an afterthought.... Like many other things in Go's approach to language design, still better than using plain old C, though.
C: Simple Defer, Ready to Use
131–140 of 157 posts
Re: C: Simple Defer, Ready to Use
#132Earlier quoted context omitted.
Why do you think you need executable stacks for nested functions? I don't know what GCC is doing, but functional programming languages usually 'compile away' the nesting of functions fairly early, see https://en.wikipedia.org/wiki/Lambda_lifting Update: Oh, I see, it's because GCC doesn't want to change how function are passed around ('function pointers' in C speak), so they need to get creative. Functional languages…
GCC creates trampolines only when it needs the address of the nested function, which it does not in this case (formally it does but not really). Also new version can place the trampoline on the heap (-ftrampline-impl=heap) and then you also need no executable stack. I wide pointer would be better though.
Wouldn't you need an executable heap, though?
(That's not as outlandish, because eg a JIT would need that to. But most straightforward C programs don't need to create any executable memory contents at runtime.)
Re: C: Simple Defer, Ready to Use
#133Earlier quoted context omitted.
When constructing an object in C, we may need some permanent sub-objects, and some temporary objects. If we only need permanent sub-objects, then we set those up gradually, and build an error path in reverse order (with gotos or with the arrow pattern); upon success, the sub-objects' ownership is transferred to the new super-object, and the new super-object is returned, just before the error path is reached. Otherwis…
So, you recommend using goto, which indeed is the only available mechanism.
There are other options, but none of them is better, IMO. You can use nested functions:
char * p = malloc(10);
if(p!=0) {
doTheRealWork(p);
};
free(p);
In gcc, doTheRealWork can be a nested function or it you can force them to be inlined.You can also (more readable than that first alternative, IMO) wrap code in a single-iteration for or while loop and then use break to exit it:
char *p = null;
for(int i=0;i==0;i=1) {
p = malloc(10);
if(p==0) break;
…
}
free(p);
Both will get uglier if you need to free multiple resources, but will work.Re: C: Simple Defer, Ready to Use
#134The C++ lambda version can be a foot gun: https://godbolt.org/z/Wd66GcrdG , if the return value is a struct, NRVO (named return value optimization) may be applied and the lambda will be called in different order. As for the n3434 proposal, given that the listed implementation experiences are all macro-based, wouldn't it be more easily adopted if proposed as a standard macro like ?
returns_struct looks actually correct to me, ie it is expected (by me). Golang's defer works this way.
Do both examples follow standard? Or is it common misinterpretation by all compilers?
Re: C: Simple Defer, Ready to Use
#135Earlier quoted context omitted.
GCC creates trampolines only when it needs the address of the nested function, which it does not in this case (formally it does but not really). Also new version can place the trampoline on the heap (-ftrampline-impl=heap) and then you also need no executable stack. I wide pointer would be better though.
> Also new version can place the trampoline on the heap (-ftrampline-impl=heap) and then you also need no executable stack. I wide pointer would be better though. Wouldn't you need an executable heap, though? (That's not as outlandish, because eg a JIT would need that to. But most straightforward C programs don't need to create any executable memory contents at runtime.)
Re: C: Simple Defer, Ready to Use
#136Earlier quoted context omitted.
No it doesn't; C functions don't set up appropriate registrations with the C++ exception / stack unwinding systems, thus C functions are simply skipped over on the way up towards the nearest exception handler. __attribute__((cleanup(…))) is purely a scope-local mechanism, it has absolutely nothing to do with exceptions.
"If -fexceptions is enabled, then cleanup_function is run during the stack unwinding that happens during the processing of the exception. Note that the cleanup attribute does not allow the exception to be caught, only to perform an action. It is undefined what happens if cleanup_function does not return normally." [1] While it's true that it -fexceptions is disabled for C by default, some C libraries need to enable i…
> some C libraries need to enable it anyway if they want to interact with C++ exceptions this way. For example C++ requires that qsort and bsearch propagate the exception thrown by the comparison callback normally
-fexceptions is not needed for this, C++ exceptions will just transparently bubble through C functions without its use.
I don't think I know of any project using -fexceptions… time to google…
Re: C: Simple Defer, Ready to Use
#137Earlier quoted context omitted.
No it doesn't; C functions don't set up appropriate registrations with the C++ exception / stack unwinding systems, thus C functions are simply skipped over on the way up towards the nearest exception handler. __attribute__((cleanup(…))) is purely a scope-local mechanism, it has absolutely nothing to do with exceptions.
GCC has comon exception handling across its supported languages. They have exceptions as a C extension that interoperate with C++. Cleanups get called during a stack unwind because they are considered the same as a destructor.
Re: C: Simple Defer, Ready to Use
#138Earlier quoted context omitted.
"If -fexceptions is enabled, then cleanup_function is run during the stack unwinding that happens during the processing of the exception. Note that the cleanup attribute does not allow the exception to be caught, only to perform an action. It is undefined what happens if cleanup_function does not return normally." [1] While it's true that it -fexceptions is disabled for C by default, some C libraries need to enable i…
Ah, I forgot about -fexceptions, Thanks. Though I need to point out it's non-default and rarely used, and in particular: > some C libraries need to enable it anyway if they want to interact with C++ exceptions this way. For example C++ requires that qsort and bsearch propagate the exception thrown by the comparison callback normally -fexceptions is not needed for this, C++ exceptions will just transparently bubble th…
Re: C: Simple Defer, Ready to Use
#139The C++ lambda version can be a foot gun: https://godbolt.org/z/Wd66GcrdG , if the return value is a struct, NRVO (named return value optimization) may be applied and the lambda will be called in different order. As for the n3434 proposal, given that the listed implementation experiences are all macro-based, wouldn't it be more easily adopted if proposed as a standard macro like ?
#define __DEFER__(V) __df_st const V = [&](void)->void #define defer __DEFER(__COUNTER__) #define __DEFER(N) __DEFER_(N) #define __DEFER_(N) __DEFER__(__DEFER_VARIABLE_ ## N)
#include
struct S { int r; ~S(){} };
(i know hn will mangle this but i won't indent this on mobile...)
people really write cpp like this or is this a intentionally obscure example?
Re: C: Simple Defer, Ready to Use
#140Earlier quoted context omitted.
I use a few strategies instead of goto: (1) For simpler cases, wrap in do {} while (0) and break from the loop (2) For multiple cleanups, use same technique combined with checks to see if the cleanup is required. E.g. if (f != null) fclose (f) (3) put the rest of the stuff in another function so that the exit code must run on the way out. In 35 years of coding C/C++, I've literally never resorted to goto. While conve…
How do you handle breaking out of a nested for loop without goto? for (…) { for (…) { if (…) { … goto found; } } } found: This is straightforward with goto and may even be vectorizable. I guess you could move the loop to a separate function or add additional flags to each loop, but neither of these seems like an improvement.