Live data from Hacker News

Defer available in gcc and clang

gustedt.wordpress.com

121–130 of 262 posts

Re: Defer available in gcc and clang

#121

Such addition is great. But there is something even better - destructors in C++. Anyone who writes C should consider using C++ instead, where destructors provide a more convenient way for resources freeing.

i write C++ every day (i actually like it...) but absolutely no one is going to switch from C to C++ just for dtors.

Of course not! Those that would have, already did!

Re: Defer available in gcc and clang

#122
post #113
post #97

Earlier quoted context omitted.

People often say that "RAII" is kind of a misnomer; the real power of RAII is deterministic destruction. And I agree with this sentiment; resource acquisition is the boring part of RAII, deterministic destruction is where the utility comes from. In that sense, there's a clear analogy between RAII and defer. But yeah, RAII can only provide deterministic destruction because resource acquisition is initialization. As lo…

In my opinion, it's the initialization part of RAII which is really powerful and still missing from most other languages. When implemented properly, RAII completely eliminates a whole class of bugs related to uninitialized or partially initialized objects: if all initialization happens during construction, then you either have a fully initialized correct object, or you exit via an exception, no third state. Additiona…

This sounds like a nice theoretical benefit to a theoretical RAII system (or even a practical benefit to RAII in Rust), but in C++, I encounter no end of bugs related to uninitialized or partially initialized objects. All primitive types have a no-op constructor, so objects of those types are uninitialized by default. Structs containing members of primitive types can be in partially initialized states where some members are uninitialized because of a missing '= 0'.

It's not uncommon that I encounter a bug when running some code on new hardware or a new architecture or a new compiler for the first time because the code assumed that an integer member of a class would be 0 right after initialization and that happened to be true before. ASan helps here, but it's not trivial to run in all embedded contexts (and it's completely out of the question on MCUs).

Re: Defer available in gcc and clang

#123
post #111

In C I just used goto - you put a cleanup section at the bottom of your code and your error handling just jumps to it. #define RETURN(x) result=x;goto CLEANUP void myfunc() { int result=0; if (commserror()) { RETURN(0); } ..... /* On success */ RETURN(1); CLEANUP: if (myStruct) { free(myStruct); } ... return result } The advantage being that you never have to remember which things are to be freed at which particular…

But you have to give this cleanup jump label a different name for every function.

Re: Defer available in gcc and clang

#124
post #118
post #71

Earlier quoted context omitted.

I feel like C people, out of anyone, should respect the code gen wins of defer. Why would you rely on runtime conditional branches for everything you want cleaned up, when you can statically determine what cleanup functions need to be called? In any case, the biggest advantage IMO is that resource acquisition and cleanup are next to each other. My brain understands the code better when I see "this is how the resource…

>"this is how the resource is acquired, this is how the resource will be freed later" Lovely fairy tale. Now can you tell me how you love to scroll back and examine all the defer blocks within a scope when it ends to understand what happens at that point?

I don't typically do that. In 99.999% of cases, 'defer free(something)' was done because it's the correct thing to do at every exit point, so I don't need to think about it at the end of the block.

Re: Defer available in gcc and clang

#125
post #122
post #113

Earlier quoted context omitted.

In my opinion, it's the initialization part of RAII which is really powerful and still missing from most other languages. When implemented properly, RAII completely eliminates a whole class of bugs related to uninitialized or partially initialized objects: if all initialization happens during construction, then you either have a fully initialized correct object, or you exit via an exception, no third state. Additiona…

This sounds like a nice theoretical benefit to a theoretical RAII system (or even a practical benefit to RAII in Rust), but in C++, I encounter no end of bugs related to uninitialized or partially initialized objects. All primitive types have a no-op constructor, so objects of those types are uninitialized by default. Structs containing members of primitive types can be in partially initialized states where some memb…

You're talking about the part of C++ that was inherited from C. Unfortunately, it was way too late to fix by the time RAII was even invented

Re: Defer available in gcc and clang

#126
post #111

In C I just used goto - you put a cleanup section at the bottom of your code and your error handling just jumps to it. #define RETURN(x) result=x;goto CLEANUP void myfunc() { int result=0; if (commserror()) { RETURN(0); } ..... /* On success */ RETURN(1); CLEANUP: if (myStruct) { free(myStruct); } ... return result } The advantage being that you never have to remember which things are to be freed at which particular…

> The advantage being that you never have to remember which things are to be freed at which particular error state.

You also don't have to remember this when using defer. That's the point of defer - fire and forget.

Re: Defer available in gcc and clang

#127
post #86

Earlier quoted context omitted.

Explain why? Have you used C99 designated init vs other languages? E.g. neither Rust, Zig nor C++20 can do this: https://github.com/floooh/sokol-samples/blob/51f5a694f614253... Odin gets really close but can't chain initializers (which is ok though): https://github.com/floooh/sokol-odin/blob/d0c98fff9631946c11...

In general it would help if you would spend some text on describing what features of C99 are missing in other languages. Giving some code and assume that the reader will figure it out is not very effective. As far as I can tell, Rust can do what it is in your example (which different syntax of course) except for this particular way of initializing an array. To me, that seems like a pretty minor syntax issue to that c…

I can show more code examples instead:

E.g. notice how here in Rust each nested struct needs a type annotation, even though the compiler could trivially infer the type. Rust also cannot initialize arrays with random access directly, it needs to go through an expression block. Finally Rust requires `..Default::default()`:

https://github.com/floooh/sokol-rust/blob/f824cd740d2ac96691...

Zig has most of the same issues as Rust, but at least the compiler is able to infer the nested struct types via `.{ }`:

https://github.com/floooh/sokol-zig/blob/17beeab59a64b12c307...

I don't have C++ code around, but compared to C99 it has the following restrictions:

- designators must appear in order (a no-go for any non-trivial struct)

- cannot chain designators (e.g. `.a.b.c = 123`)

- doesn't have the random array access syntax via `[index]`

> ...like a pretty minor syntax issue...

...sure, each language only has a handful minor syntax issues, but these papercuts add up to a lot of syntax noise to sift through when compared to the equivalent C99 code.

Re: Defer available in gcc and clang

#128
post #111

In C I just used goto - you put a cleanup section at the bottom of your code and your error handling just jumps to it. #define RETURN(x) result=x;goto CLEANUP void myfunc() { int result=0; if (commserror()) { RETURN(0); } ..... /* On success */ RETURN(1); CLEANUP: if (myStruct) { free(myStruct); } ... return result } The advantage being that you never have to remember which things are to be freed at which particular…

But you have to give this cleanup jump label a different name for every function.

You don't. Labels are local to functions in C.

Re: Defer available in gcc and clang

#129
post #125
post #122

Earlier quoted context omitted.

This sounds like a nice theoretical benefit to a theoretical RAII system (or even a practical benefit to RAII in Rust), but in C++, I encounter no end of bugs related to uninitialized or partially initialized objects. All primitive types have a no-op constructor, so objects of those types are uninitialized by default. Structs containing members of primitive types can be in partially initialized states where some memb…

You're talking about the part of C++ that was inherited from C. Unfortunately, it was way too late to fix by the time RAII was even invented

And the consequence is that, at least in C++, we don't see the benefit you describe of "objects can never be in an uninitialized or partially-initialized state".

Anyway, I think this could be fixed, if we wanted to. C just describes the objects as being uninitialized and has a bunch of UB around uninitialized objects. Nothing in C says that an implementation can't make every uninitialized object 0. As such, it would not harm C interoperability if C++ just declared that all variable declarations initialize variables to their zero value unless the declaration initializes it to something else.

Re: Defer available in gcc and clang

#130
post #111

In C I just used goto - you put a cleanup section at the bottom of your code and your error handling just jumps to it. #define RETURN(x) result=x;goto CLEANUP void myfunc() { int result=0; if (commserror()) { RETURN(0); } ..... /* On success */ RETURN(1); CLEANUP: if (myStruct) { free(myStruct); } ... return result } The advantage being that you never have to remember which things are to be freed at which particular…

defer is a stack, scope local and allows cleanup code to be optically close to initialization code.
Post reply on HN