top | item 44692806

(no title)

cyber1 | 7 months ago

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.

discuss

order

sparkie|7 months ago

The biggest issue is the ABI for C - it's the lingua-franca of language interoperability and can't really be changed - so whatever approach is taken it needs to be fully compatible with the existing ABI. `_Generic` is certainly flawed but doesn't cause any breaking ABI changes.

That's also a major reason why you'd use C rather than C++. The C++ ABI is terrible for language interoperability. It's common for C++ libraries to wrap their API in C so that it can be used from other language's FFIs.

Aside from that another reason we prefer C to C++ is because we don't want vtables. I think there's room for a `C+` language, by which I mean C+templates and not C+classes - perhaps with an ABI which is a subset of the C++ ABI but superset of the C ABI.

IAmLiterallyAB|7 months ago

> we don't want vtables

Then don't use virtual functions. Then there will be no vtables.

You might have known that already, but in general I'm surprised how many engineers think that all C++ classes have vtables. No, most in fact do not. C++ classes generally have the same memory layout as a C struct as long as you don't use virtual functions.

signa11|7 months ago

> I think there's room for a `C+` language, by which I mean C+templates and not C+classes - perhaps with an ABI which is a subset of the C++ ABI but superset of the C ABI.

indeed, i have spoken to a lot of my colleagues about just that. if overloading is not allowed, perhaps there is still some hope for a backwards compatible abi ?

cyber1|7 months ago

This is true. I agree with this statement. It's the holy cow of C. However, the problem with generic programming and metaprogramming isn't going away, and many people continue to struggle with it. Introducing something like compile-time reflection might be a solution...

up2isomorphism|7 months ago

They showed something they think it’s neat. You start a topic with the assumption that they struggle, not sure how you get that information from the original post or you just want to state that claim anyway?

sirwhinesalot|7 months ago

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...

uecker|7 months ago

There are less annoying ways to implement this in C. There are at least two different common approaches which avoid having macro code for the generic functions:

The first is to put this into an include file

  #define type_argument int
  #include <vector.h>
Then inside vector.h the code looks like regular C code, except where you insert the argument.

  foo_ ## type_argument ( ... )
The other is to write generic code using void pointers or container_of as regular functions, and only have one-line macros as type safe wrappers around it. The optimizer will be able to specialize it, and it avoids compile-time explosion of code during monomorphization,

I do not think that templates are less annoying in practice. My experience with templates is rather poor.

cyber1|7 months ago

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?

SAI_Peregrinus|7 months ago

The name has to be ugly, new names in C are always taken from the set of reserved identifiers: those starting with an underscore & a capital letter, or with two underscores. Since they didn't reserve any "normal" names, all new keywords will be stuff like `_Keyword` or `__keyword`, unless they break backwards compatibility. And they really hate breaking backwards compatibility, so that's quite unlikely.

uecker|7 months ago

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

cyber1|7 months ago

I'm currently at a crossroads: C++ or Zig. One is very popular with a large community, amazing projects, but has lots of ugly design decisions and myriad rules you must know (this is a big pain, it seems like even Stroustrup can't handle all of them). The other is very close to what I want from C, but it's not stable and not popular.

rurban|7 months ago

Macros are the best possibly approach, compared to C++ templates or _Generic