Live data from Hacker News

My personal C coding style as of late 2023

nullprogram.com

251–260 of 466 posts

Re: My personal C coding style as of late 2023

#251
post #57

Earlier quoted context omitted.

(self-reply) One more thing. > I could use _Bool, but I’d rather stick to a natural word size and stay away from its weird semantics. This is even more subjective, but personally I like _Bool's semantics. They mean that if an expression works in an `if` statement: if (flags & FLAG_ALLOCATED) then you can extract that same expression into a boolean variable: _Bool need_free = flags & FLAG_ALLOCATED; The issue is that…

This is all very good and very, ahem, true. But (and it's a big butt); if (need_free == true) Is such a horrible code smell to me. You have a perfectly good boolean. Why compare it to a second boolean to get a third boolean? if (need_free) or if (!need_free) for the opposite case is so much better. I will admit that in my world this leaves if (need_free == some_other_bool) as something I don't have a particularly com…

  > if (need_free == true)
  > Is such a horrible code smell to me. You have a perfectly good boolean. Why compare it to a second boolean to get a third boolean?
  > if (need_free)
You are probably interested if the `need_free` flag is set to true, and not if `need_free`. It is true that `if (need_free)` has the same behaviour, but it is some steps farther from what you are interested in.

Re: My personal C coding style as of late 2023

#252

Earlier quoted context omitted.

The reality is that C `int` is 32 bits in size. Sure, that's not true for 16 bit targets. But are you really going to port a 5Mb program to 16 bits? It's not worth worrying about. Your code is highly unlikely to be portable to 16 bits anyway. The problem is with `long`, which is 32 bits on some machines and 64 bits on others. This is just madness. Fortunately, `long long` is always 64 bits, so it makes sense to just…

Yet another issue is that `char` is signed on some platforms but unsigned on others. It is signed on x86 but unsigned on RISC-V. On ARM it could be either (ARM standard is unsigned, Apple does signed). I therefore use typedefs called `byte` and `ubyte` wherever the data is 8-bit but not character data. I also use the aliases `ushort`, `uint` and `ulong` to cut down on typing. On the other hand, the types in are often…

Default signed/unsigned is not a platform convention but a compiler one. You can change it with a compiler switch, in the makefile.

Re: My personal C coding style as of late 2023

#253
> typedef char byte;

That one is dubious. Char has magic aliasing properties that uint8_t might not have (iirc that was contentious in a GCC bug report) and it will be signed on some platforms and unsigned on others, which changes implicit integer conversions.

Missing from this is to embrace attribute((overloadable)) and attribute((cleanup)).

Overloadable is the sane, useful alternative to the thing standardised as _Generic. The C _Generic will let you define an overload set, with some weirdness around type conversions, provided you write the entire set out as a single _Generic expression, probably wrapped in a macro. If you want to dispatch on more than one argument, you nest _Generic expressions. If you want to declare different functions in different headers - maybe you want 'size(T)' defined on various types in the codebase - you can't. If you don't like the idea of thousands of lines of distracting nonsense in the preprocessed output, tough. Or - use overloadable, get open overload sets, minimal compile time cost, obvious intermediate IR, everything works. Prior art is all of C++, so talking decades of the tooling learning to deal with it.

Cleanup is either a replacement for raii, or a means to have debug builds yell at you when you miss a free. It looks like that got warped into a thing called 'defer' with different behaviour that didn't make it through the committee last time.

Other than that, ad hoc code generators work really well with C. Especially if you're willing to use some compiler extensions. Code generators + overloadable will give a fair approximation to templated data structures without going deep into the insanity of the preprocessor. If the overloadable functions are static inline forwarding things in a header they don't even mess up symbol names; you just get a straightforward translation to vector_float_size or whatever.

Personally I've given up on ISO C. I'd quite like to code in a dialect of C99 with a few of the GNU extensions and the equivalent of `fno-strict-aliasing`, but C with the pointer provenance modelling and an accretion of C++ features has no personal value. Currently still using clang with flags to make it behave like that but I'm conscious that's on borrowed time - the application performance friendly aliasing rules are the default and gaining popularity, and relying on opt-out flags is a means of opting into compiler bugs.

Semi-actively seeking something that will let me write assembly without the hassle of manual register allocation and calling conventions. Old style C with some of the warts bashed off would be good for that.

Re: My personal C coding style as of late 2023

#254

Earlier quoted context omitted.

The author did qualify it with personal coding style. Frankly the standard types are too verbose and I wish this guy's elegant and clear list had been the one that was adopted way back when.

That ship has sailed ages ago. There are some things you should just accept about C, or any programming language really. Just because you can do something doesn't mean you should do something. I don't know how many years of experience in C this guy has, but this is a "been there, done that" case for me. I stick to stdint and stdbool today, and even if only half the code/libs I interface with do that, it's already wor…

I use very similar types as the author in my own libs and framework.

I think this is perfectly legitimate, in the same way that I don't use std libs directly but always behind wrappers or my own implementation.

The C std lib and default types are often what is keeping the language back.

And they should be used when you have no other choice.

Short name for scalar types is also pretty much the new standard for modern languages such as Zig.

Re: My personal C coding style as of late 2023

#255

IMO, defining your own types is one step too far. Now everyone who is already familiar with C types has to learn your own quirky system to understand one program. I think it does probably make sense to be specific about the sizes though e.g. using uint32_t over just uint (and expecting to receive some architecture-dependent size you might not get with uint.) These types should be defined in the right header (I think…

IMO, defining your own aliases for stdint.h types is innocent enough, but manually defining prototypes for Win32 calls instead of including standard headers is one step too far. You don’t own the ABI here: things like HANDLE and WPARAM have already changed sizes once, who’s to say ULONG_PTR will stay the same as uintptr_t for all future architectures? There is a good reason why doing the same on Unix is discouraged.

Re: My personal C coding style as of late 2023

#256

IMO, defining your own types is one step too far. Now everyone who is already familiar with C types has to learn your own quirky system to understand one program. I think it does probably make sense to be specific about the sizes though e.g. using uint32_t over just uint (and expecting to receive some architecture-dependent size you might not get with uint.) These types should be defined in the right header (I think…

It's pretty much the same types you see in Rust or Zig, and I think Linux even uses some of the same types.

With slightly different semantics; as I recall, the Linux uXX and iXX types have natural alignment (equal to size), while stdint.h types are not required to.

Re: My personal C coding style as of late 2023

#257

IMO, defining your own types is one step too far. Now everyone who is already familiar with C types has to learn your own quirky system to understand one program. I think it does probably make sense to be specific about the sizes though e.g. using uint32_t over just uint (and expecting to receive some architecture-dependent size you might not get with uint.) These types should be defined in the right header (I think…

The reality is that C `int` is 32 bits in size. Sure, that's not true for 16 bit targets. But are you really going to port a 5Mb program to 16 bits? It's not worth worrying about. Your code is highly unlikely to be portable to 16 bits anyway. The problem is with `long`, which is 32 bits on some machines and 64 bits on others. This is just madness. Fortunately, `long long` is always 64 bits, so it makes sense to just…

C is also used on embedded. It's even there the default language.

Re: My personal C coding style as of late 2023

#258

Earlier quoted context omitted.

In that case D should probably start to have an internal conversation about what they're going to call 128 bits then, 'cause its going to become a thing sooner or later. stdint already has that covered though: (u)int128_t

it has defined the type for very long: it's cent and ucent. It hasn't implemented it completely though, but named and defined it is already forever.

That's really interesting, and for me a totally unexpected name, having never seen that nomenclature before - would be interesting to see how consensus around that was arrived at - but hey, we gotta call it something! (But not DoubleQuadWord please ... )

Re: My personal C coding style as of late 2023

#259

I get that everyone has their own coding style, but ditching established conventions in C for personal aesthetic seems a bit much. Like, using u8 or i32 instead of the standard uint8_t or int32_t might save a few keystrokes, but it could confuse anyone else looking at the code. And the custom string type over null-terminated strings? C's built around those, and deviating from that just feels like making life harder f…

u16 etc show up a lot and are unlikely to confuse programmers.

Where it does go to pieces is when two different programs both define u16, use them in header files, and then a third program tries to include both those header files at the same time. The big advantage of is avoiding that failure mode.

The namespaced library type equivalent is something like libname_u32, at which point it's tempting to write uint32_t instead of the libname:: or libname_ prefix.

Re: My personal C coding style as of late 2023

#260

IMO, defining your own types is one step too far. Now everyone who is already familiar with C types has to learn your own quirky system to understand one program. I think it does probably make sense to be specific about the sizes though e.g. using uint32_t over just uint (and expecting to receive some architecture-dependent size you might not get with uint.) These types should be defined in the right header (I think…

Just a note: defining own integer types has sense for resource-limited platforms. Most common type I see is something like "dim_t", which is 32-bit or 64-bit depending on use-case. 32-bit integers are often used even on 64-bit platforms in pointer compression schemes (for example, allocate your own heap and only store 32-bit offsets). This not only gives 2x improvement on memory usage for <4GB workloads, but it also improves performance due to better cache locality.
Post reply on HN