Live data from Hacker News

Some C habits I employ for the modern day

unix.dog

71–80 of 162 posts

Re: Some C habits I employ for the modern day

#71
post #63

Earlier quoted context omitted.

I can't think of a language that isn't simpler compared to C++

Might be, then again C23 isn't K&R C that many still learn from.

> Might be, then again C23 isn't K&R C that many still learn from.

I agree with this, but then again, not many people are learning C now anyway. It will die away from natural attrition anyway, is my point.

The K&R C does have a few advantages, because the compilers at the time were not so aggressive in optimisation, and will consistently emit code that (for example) performed a NULL dereference (or other UB), ensuring things like consistently crashing instead of silently losing data/doing the wrong thing.

Re: Some C habits I employ for the modern day

#72
post #61
post #17

If you really insist on not having a distinction between "u8"/"i8" and "unsigned char"/"signed char", and you've gone to the trouble of refusing to accept CHAR_BIT!=8, I'm pretty sure it'd be safer to typedef unsigned char u8 and typedef signed char i8. uint8_t/int8_t are not necessarily character types (see 6.2.5.20 and 7.22.1.1) and there are ramifications (see, e.g., 6.2.6.1, 6.3.2.3, 6.5.1).

> and you've gone to the trouble of refusing to accept CHAR_BIT!=8 This one was a head-scratcher for me. Yeah, there's no cost to check for it, but architectures where CHAR_BIT != 8 are rarer even than 24-bit architectures.

I got the impression the author was implying because CHAR_BIT is enforced to be 8 that uint8_t and char are therefore equivalent, but they are different types with very different rules.

E.g. `char p = (char )&astruct` may violate strict aliasing but `uint8_t p = (uint8_t )&astruct` is guaranteed legal. Then modulo, traps, padding, overflow, promotion, etc.

Re: Some C habits I employ for the modern day

#73

> and I end up having all these typedefs in my projects I avoid doing this now. It's more trouble than it's worth and it changes your code from a standard dialect of C into a custom one. Plus my eyes are old and they don't enjoy separating short identifiers. > typedef struct { ... } String I avoid doing this. Just use `struct string { ... };'. It makes it clear what you're handling. C23 finally gave us "auto", you sh…

> So you use strlen() a lot and don't have to deal with multibyte characters anywhere in your code. It's not much of a strategy. You don't need to support all multibyte encodings (i.e. DBCS, UCS-2, UCS-4, UTF-16 or UTF-32) characters if you're able to normalise all input to UTF-8. I think, when you are building a system, restricting all (human language) input to be UTF-8 is a fair and reasonable design decision, and…

Am I missing something here? UTF8 has multibyte characters, they're just spread across multiple bytes.

When you strlen() a UTF8 string, you don't get the length of the string, but instead the size in bytes.

Same with indices. If you Index at [1] in a string with a flag emoji, you don't get a valid UTF8 code point, but instead some part of the flag emoji. This applies with any UTF8 code points larger than 1 byte, which there are a lot of.

UTF16 or UTF32 are just different encodings.

What am I missing?

That's why UTF8 libraries exist.

Re: Some C habits I employ for the modern day

#74
post #73

Earlier quoted context omitted.

> So you use strlen() a lot and don't have to deal with multibyte characters anywhere in your code. It's not much of a strategy. You don't need to support all multibyte encodings (i.e. DBCS, UCS-2, UCS-4, UTF-16 or UTF-32) characters if you're able to normalise all input to UTF-8. I think, when you are building a system, restricting all (human language) input to be UTF-8 is a fair and reasonable design decision, and…

Am I missing something here? UTF8 has multibyte characters, they're just spread across multiple bytes. When you strlen() a UTF8 string, you don't get the length of the string, but instead the size in bytes. Same with indices. If you Index at [1] in a string with a flag emoji, you don't get a valid UTF8 code point, but instead some part of the flag emoji. This applies with any UTF8 code points larger than 1 byte, whic…

> When you strlen() a UTF8 string, you don't get the length of the string, but instead the size in bytes.

Yes, and?

> What am I missing?

A use-case? Where, in your C code, is it reasonable to get the number of multibyte characters instead of the number of bytes in the string?

What are you going to use "number of unicode codepoints" for?

Any usage that amounts to "I need the number of unicode codepoints in this string" is coupled to handling the display of glyphs within your program, in which case you'd be using a library for that anyway because graphics is not part of C (or C++) anyway.

If you're simply printing it out, storing it, comparing it, searching it, etc, how would having the number of unicode codepoints help? What would it get used for?

Re: Some C habits I employ for the modern day

#75

I'm a huge fan of the 'parse, don't validate' idiom, but it feels like a bit of a hurdle to use it in C - in order to really encapsulate and avoid errors, you'd need to use opaque pointers to hidden types, which requires the use of malloc (or an object pool per-type or some other scaffolding, that would get quite repetitive after a while, but I digress). You basically have to trade performance for correctness, wherea…

You can play tricks if you’re willing to compromise on the ABI:

    typedef struct foo_ foo;
    enum { FOO_SIZE = 64 };
    foo *foo_init(void *p, size_t sz);
    void foo_destroy(foo *p);
    #define FOO_ALLOCA() \
      foo_init(alloca(FOO_SIZE), FOO_SIZE)
Implementation (size checks, etc. elided):

    struct foo_ {
        uint32_t magic;
        uint32_t val;
    };
    
    foo *foo_init(void *p, size_t sz) {
        foo *f = (foo *)p;
        f->magic = 1234;
        f->val = 0;
        return f;
    }
Caller:

    foo *f = FOO_ALLOCA();
    // Can’t see inside
    // APIs validate magic

Re: Some C habits I employ for the modern day

#76
post #61

Earlier quoted context omitted.

> and you've gone to the trouble of refusing to accept CHAR_BIT!=8 This one was a head-scratcher for me. Yeah, there's no cost to check for it, but architectures where CHAR_BIT != 8 are rarer even than 24-bit architectures.

I got the impression the author was implying because CHAR_BIT is enforced to be 8 that uint8_t and char are therefore equivalent, but they are different types with very different rules. E.g. `char p = (char )&astruct` may violate strict aliasing but `uint8_t p = (uint8_t )&astruct` is guaranteed legal. Then modulo, traps, padding, overflow, promotion, etc.

[deleted]

Re: Some C habits I employ for the modern day

#77
post #67

I'm a huge fan of the 'parse, don't validate' idiom, but it feels like a bit of a hurdle to use it in C - in order to really encapsulate and avoid errors, you'd need to use opaque pointers to hidden types, which requires the use of malloc (or an object pool per-type or some other scaffolding, that would get quite repetitive after a while, but I digress). You basically have to trade performance for correctness, wherea…

> But then anyone could just instantiate an invalid Name without calling the parse_name function and pass it around wherever This is nothing new in C. This problem has always existed by virtue of all struct members being public. Generally, programmers know to search the header file / documentation for constructor functions, instead of doing raw struct instantiation. Don‘t underestimate how good documentation can driv…

In C++ you would have a protected constructor and related friend utility class to do the parsing, returning any error code, and constructing the thing, populating an optional, shared_ptr, whatever… don’t make constructors fallible.

Re: Some C habits I employ for the modern day

#78

> In the absence of proper language support, “sum types” are just structs with discipline. With enough compiler support they could be more than that. For example, I submitted a tagged union analysis feature request to gcc and clang, and someone generalized it into a guard builtin. https://github.com/llvm/llvm-project/issues/74205 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112840 GCC proved to be too complex for me…

FWIW, Coverity (maybe others) has a checker that creates an error if it detects tagged union access without first checking the tag. It’s not as strict as enforcing which fields belong to which tag values, but it can still be useful. I’d much rather have what was proposed in the GCC bug!

Re: Some C habits I employ for the modern day

#79
post #6

Regarding memory, I recently changed to try to not use dynamic memory, or if I need to, to do it once at startup. Often static memory on startup is sufficient. Instead use the stack much more and have a limit on how much data the program can handle fixed on startup. It adds the need to think what happens if your system runs out of memory. Like OP said, it's not a solution for all types of programs. But it makes for v…

I have some firmware that runs an event loop. There is no malloc anywhere. But I do have an area which gets reset event handler after each call. Useful for passing objects up the call stack.

One other thing I tend to do anything that needs to live longer than the current call stack gets copied into a queue of some sort. I feel it's kinda doing manually what rusts borrow checker tries to enforce.

Re: Some C habits I employ for the modern day

#80

Earlier quoted context omitted.

There is some irony in someone replying to the author of the D language suggesting that maybe the D language is the real solution he's looking for.

It might be the language he is looking for, but it might not, and more likely than not is not. D is one of those odd languages which most likely ought to have gotten a lot more popular than it did, but for one reason or another, never quite caught on. Perhaps one reason is because it lacks a sense of eccentricity and novelty that other languages in its weight class have. Or perhaps it's just too unfamiliar in all the…

GP literally invented the D language.
Post reply on HN