Live data from Hacker News

My review of the C standard library in practice

nullprogram.com

31–40 of 94 posts

Re: My review of the C standard library in practice

#31
post #7

>Without libc you don’t have to use this global, hopefully thread-local, pseudo->variable. Good riddance. Return your errors, and use a struct if necessary. On contrary you should absolutely use errno, if only to report your IO errors. Or maybe he never does IO. After all a program that doesn't output anything is likely safer, since these are all evil side effects. I can just see that new programming paradigm: "side…

practice reading with understanding mate

Re: My review of the C standard library in practice

#32

Earlier quoted context omitted.

It really doesn't, though, although in principle Editions could provide an escape hatch it would be so drastic as to be largely unthinkable. Because of Rust's commitment to long term stability all of the standard library is there forever, even if it's a mistake and thus deprecated. std::u8::MAX will be in the library forever, even though you can write u8::MAX to get the same constant, name.trim_left_matches(remove) w…

Hey, don't get me started on semantics... what if you trim from the left or right, but are using RTL. Imagine your surprise when the exact opposite happens.

Which is exactly why, as GP noted, rust deprecated trim_left/trim_right and added trim_start/trim_end.

Re: My review of the C standard library in practice

#33
post #22
post #17

Earlier quoted context omitted.

errno is thread-local, so you don’t have to worry about context switches.

Historically, it wasn’t (of course; Unix didn’t have multiple threads in a program). https://pubs.opengroup.org/onlinepubs/7908799/xsh/errno.html : “Previously both POSIX and X/Open documents were more restrictive than the ISO C standard in that they required errno to be defined as an external variable, whereas the ISO C standard required only that errno be defined as a modifiable lvalue with type int.” Even now that…

That library functions don’t reset errno is considered a feature / convenience, it allows calling a bunch of them then checking if the entire thing succeeded or failed.

Obviously this assumes the failure of one function does not trigger a UB down the line, and that you care about general but not specific failure. And also obviously this is easy to replicate with an API which returns error objects / codes.

IIRC this is most(ly) convenient in graphics APIs, I don’t know how common leveraging this in the libc actually is.

Re: My review of the C standard library in practice

#34
post #11

Earlier quoted context omitted.

If you don't understand why errno is bad then it'll be hard to explain it. Errno is fine if you're accessing it right after a stdlib function that sets it, because it's the equivalent of a function's error code. Once you get out of that context errno becomes less and less useful and probably shouldn't be used, because without that context you won't know who/what actually set it.

That’s all documented in the standard though. Why would you access errno any other way?

The main issues with errno are you need to remember to reset it before you enter a context whose failability you care about, so any code which relies on errno must be:

    errno = 0
    // code which may error here
    if(errno) { … }
And that errno is notably not scoped, so the code which may error should only be composed of calls to libc, or code whose interaction with libc you understand perfectly, otherwise you need to save and restore errno around uncontrolled calls.

This is quite verbose, annoying, and error prone.

Re: My review of the C standard library in practice

#36
post #23
post #18

Earlier quoted context omitted.

You won't find that on either ISO C nor POSIX specifications. It only happens to be implemented that way in some environments, as a macro to either thread local or some function call that retrieves the right errno. Still, even that doesn't take care about signal handling.

This [1] seems to contradict, that at least for more "recent" versions of POSIX. [1]: https://unix.org/whitepapers/reentrant.html

That isn't the most recent version, this is,

https://pubs.opengroup.org/onlinepubs/9699919799/functions/e...

Re: My review of the C standard library in practice

#37

Earlier quoted context omitted.

You do option 3, manually adapt one type to the other because they are different types. If you call one function that returns an int and need to pass it to another function that expects a char* what do you do?

I think you may have lost track of what this thread is discussing. To be more concrete, let's say you have the following code (pseudo-Go): package main import "example.com/http_server" // v2 import "example.com/utils" // transitively depends on http_server v1 function main() { var srv *http_server.Server = http_server.New() utils.ServeHttp(srv) } You're going to have a problem, because ServeHttp expects an HTTP serve…

> there's no way to import both the v1 and v2 APIs at the same time in your code, so you don't have a way to declare a conversion function.

In Go major versions have different import paths. You can import multiple major versions of the same library:

    import (
        http_server_old "example.com/http_server"
        http_server_new "example.com/http_server/v2"
    )

Re: My review of the C standard library in practice

#38
post #18

Earlier quoted context omitted.

You won't find that on either ISO C nor POSIX specifications. It only happens to be implemented that way in some environments, as a macro to either thread local or some function call that retrieves the right errno. Still, even that doesn't take care about signal handling.

Excerpt from C11: "[...] errno which expands to a modifiable lvalue that has type int and thread local storage duration"

Still doesn't cover signals, only works if C11 threads are being used (it is all open if OS threads follow the same TLS mechanism), and those C89 and C99 code bases get nothing from it anyway.

Re: My review of the C standard library in practice

#39
post #22

Earlier quoted context omitted.

Historically, it wasn’t (of course; Unix didn’t have multiple threads in a program). https://pubs.opengroup.org/onlinepubs/7908799/xsh/errno.html : “Previously both POSIX and X/Open documents were more restrictive than the ISO C standard in that they required errno to be defined as an external variable, whereas the ISO C standard required only that errno be defined as a modifiable lvalue with type int.” Even now that…

That library functions don’t reset errno is considered a feature / convenience, it allows calling a bunch of them then checking if the entire thing succeeded or failed. Obviously this assumes the failure of one function does not trigger a UB down the line, and that you care about general but not specific failure. And also obviously this is easy to replicate with an API which returns error objects / codes. IIRC this i…

It also requires code to explicitly set errno many, many times. I think there are many errors lurking in C code there, with code calling foo, not checking errno or checking it but not resetting it, later calling bar, and assuming errno was set by bar.

Re: My review of the C standard library in practice

#40
> As with volatile, C is using the type system to indirectly achieve a goal. Types are not atomic, loads and stores are atomic

I don't get this argument. This same wording can actually be used to make pointers untyped: "pointers are not inherently typed, loads and stores are typed. (In fact, LLVM IR recently made pointers untyped too)". And yet, clearly there's value in having them typed at the language level. Similarly here, there's clear value in having them part of the type system, if only to prevent a footgun of someone forgetting to mark a load as atomic/volatile.

Post reply on HN