Live data from Hacker News

Generic Containers in C: Vec

uecker.codeberg.page

21–30 of 91 posts

Re: Generic Containers in C: Vec

#21

It's amazing how many people try to write generic containers for C, when there is already a perfect solution for that, called C++. It's impossible to write generic type-safe code in C, and this version resorts to using GCC extensions to the language (note the ({…}) expressions). For those afraid of C++: you don't have to use all of it at once, and compilers have been great for the last few decades. You can easily por…

My problem with C++, and maybe this is just me, is RAII.

Now, Resource Aquisition Is Initialization is correct, but the corollary is not generally true, which is to say, my variable going out of scope does not generally mean I want to de-aquire that resource.

So, sooner or later, everything gets wrapped in a reference counting smart pointer. And reference counting always seemed to me to be a primitive or last-resort memory managment strategy.

Re: Generic Containers in C: Vec

#22
Many C programmers need proper generic programming mechanisms (perhaps something like Zig's comptime) in C, but macros are the worst possible approach, and they don't want to switch to a different language like C++. As a result, they struggle with these issues. This is what I think the standardization committee should focus on, but instead, they introduced _Generic.

Re: Generic Containers in C: Vec

#24
post #22

Many C programmers need proper generic programming mechanisms (perhaps something like Zig's comptime) in C, but macros are the worst possible approach, and they don't want to switch to a different language like C++. As a result, they struggle with these issues. This is what I think the standardization committee should focus on, but instead, they introduced _Generic.

The most insulting thing about _Generic is the name. Really? _Generic? For a type-based switch with horrific syntax? What were they thinking...

That said, generic programming in C isn't that bad, just very annoying.

To me the best approach is to write the code for a concrete type (like Vec_int), make sure everything is working, and then do the following:

A macro Vec(T) sets up the struct. It can then be wrapped in a typedef like typedef Vec(int) Vec_i;

For each function, like vec_append(...), copy the body into a macro VEC_APPEND(...).

Then for each relevant type T: copy paste all the function declarations, then do a manual find/replace to give them some suffix and fill in the body with a call to the macro (to avoid any issues with expressions being executed multiple times in a macro body).

Is it annoying? Definitely. Is it unmanageable? Not really. Some people don't even bother with this last bit and just use the macros to inline the code everywhere.

Some macros can delegate to void*-based helpers to minimize the bloating.

EDIT: I almost dread to suggest this but CMake's configure_file command works great to implement generic files...

Re: Generic Containers in C: Vec

#25
post #17

Earlier quoted context omitted.

> realloc often does this already internally Is Martin claiming that realloc is "often" maintaining a O(1) growable array for us? That's what the analogous types in C++ or Rust, or indeed Java, Go, C# etc. provide.

No, I claim that the performance of realloc is good enough for most use cases because it also does not move the memory in case there is already enough space left. I then mention that for other use cases, you can maintain a capacity field only in the part of the code where you need this. Whether this is the right design for everybody, I do not know, but so far it is what I prefer for myself.

I think the claim that it's good enough for "most use cases" to have an O(n) growable array container needs some serious backing data.

Re: Generic Containers in C: Vec

#26
post #12

> Many vector types include a capacity field, so that resizing on every push can be avoided. I do not include one, because simplicity is more important to me and realloc often does this already internally. In most scenarios, the performance is already good enough. I think this is the wrong decision (for a generic array library).

Without a capacity field, each push operation potentially triggers a realloc, causing O(n) copying and possible memory fragmentation - especially problematic for large vectors or performance-critical code.

Re: Generic Containers in C: Vec

#27

It's amazing how many people try to write generic containers for C, when there is already a perfect solution for that, called C++. It's impossible to write generic type-safe code in C, and this version resorts to using GCC extensions to the language (note the ({…}) expressions). For those afraid of C++: you don't have to use all of it at once, and compilers have been great for the last few decades. You can easily por…

My problem with C++, and maybe this is just me, is RAII. Now, Resource Aquisition Is Initialization is correct, but the corollary is not generally true, which is to say, my variable going out of scope does not generally mean I want to de-aquire that resource. So, sooner or later, everything gets wrapped in a reference counting smart pointer. And reference counting always seemed to me to be a primitive or last-resort…

If you want to take back manual control, use the release() function

Re: Generic Containers in C: Vec

#28
post #17

Earlier quoted context omitted.

No, I claim that the performance of realloc is good enough for most use cases because it also does not move the memory in case there is already enough space left. I then mention that for other use cases, you can maintain a capacity field only in the part of the code where you need this. Whether this is the right design for everybody, I do not know, but so far it is what I prefer for myself.

I think the claim that it's good enough for "most use cases" to have an O(n) growable array container needs some serious backing data.

If I have some time, I will do some benchmarking.

In all my current code where I tried it makes no noticeable difference, and I am not a fan of premature optimization. But then, I could always switch to the alternative API.

Re: Generic Containers in C: Vec

#29
post #22

Many C programmers need proper generic programming mechanisms (perhaps something like Zig's comptime) in C, but macros are the worst possible approach, and they don't want to switch to a different language like C++. As a result, they struggle with these issues. This is what I think the standardization committee should focus on, but instead, they introduced _Generic.

I don't struggle, I switch from C++ to C and find this much nicer.

Re: Generic Containers in C: Vec

#30
post #22

Many C programmers need proper generic programming mechanisms (perhaps something like Zig's comptime) in C, but macros are the worst possible approach, and they don't want to switch to a different language like C++. As a result, they struggle with these issues. This is what I think the standardization committee should focus on, but instead, they introduced _Generic.

The most insulting thing about _Generic is the name. Really? _Generic? For a type-based switch with horrific syntax? What were they thinking... That said, generic programming in C isn't that bad, just very annoying. To me the best approach is to write the code for a concrete type (like Vec_int), make sure everything is working, and then do the following: A macro Vec(T) sets up the struct. It can then be wrapped in a…

Hey, I understand you and know this stuff well, having worked with it for many years as a C dev. To be honest, this isn't how things should generally be done. Macros were invented for very simple problems. Yes, we can abuse them as much as possible (for example, in C++, we discovered SFINAE, which is an ugly, unreadable technique that wasn't part of the programming language designer's intent but rather like a joke that people started abusing), but is it worth it?
Post reply on HN