Live data from Hacker News

My review of the C standard library in practice

nullprogram.com

21–30 of 94 posts

Re: My review of the C standard library in practice

#21
post #19

Earlier quoted context omitted.

So one should focus on creating versioned API IMO. Nobody does that for some reason. I should be able to declare my program to use v2023 API and consume libraries which use v1975 API.

No, one should focus on creating smaller independent modules and don't mix unrelated stuff like networking fundamentals with string convenience functions.

Are you familiar with linker hell? I agree in theory with your suggestion, but not for C or C-like languages which have such a poor story for linking compilation units.

Re: My review of the C standard library in practice

#22
post #17
post #16

Earlier quoted context omitted.

Assuming there is no thread context switch or OS signals between those calls.

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 it is guaranteed to be thread-local, it still is a bad API because of (same page)

“A program that uses errno for error checking should set it to 0 before a function call”

And yes, that still seems true today. https://en.cppreference.com/w/cpp/error/errno:

“library functions never store 0 in errno.”

Re: My review of the C standard library in practice

#23
post #18
post #17

Earlier quoted context omitted.

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

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

Re: My review of the C standard library in practice

#24

Earlier quoted context omitted.

What if the type was a "Some1975Struct" that got passed? Then that would give a huge blaring error because it's not the same type as a "Some2022Struct".

The naming convention isn't important. If the structs are from different versions of the library then you either have a type error (nominal types) or have to figure out the semantics of arbitrarily uninitialized fields.

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?

Re: My review of the C standard library in practice

#26
post #5

Earlier quoted context omitted.

So one should focus on creating versioned API IMO. Nobody does that for some reason. I should be able to declare my program to use v2023 API and consume libraries which use v1975 API.

Rust kind of does this. Language feature uses "edition" and has crates for its standard api instead of built in.

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) will exist forever even though you ought to write name.trim_start_matches(remove) because "left" assumes an LTR writing system.

If NonZeroI64 is a bad idea, too bad it's in the standard library forever. If AddAssign is a bad idea, too bad it's in the standard library forever. The language syntax is allowed to evolve via Editions, but the standard library never breaks backward compatibility.

Re: My review of the C standard library in practice

#27

Earlier quoted context omitted.

The naming convention isn't important. If the structs are from different versions of the library then you either have a type error (nominal types) or have to figure out the semantics of arbitrarily uninitialized fields.

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 server struct from the v1 API, and you're passing in a struct from the v2 API.

If you tried to write an adapter for this then (1) you'd need to somehow construct a concrete proxy of a type you don't control, and (2) 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.

That's why APIs with versions declared by dependency edges are impractical.

Re: My review of the C standard library in practice

#28
post #5

Earlier quoted context omitted.

Rust kind of does this. Language feature uses "edition" and has crates for its standard api instead of built in.

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.

Re: My review of the C standard library in practice

#29

When Go showed up on the scene, I was just about losing my patience with libc, and one if the things I found miraculous about Go was "no more strtok and friends".

I found it liberating to ignore strtok and friends even when coding in C :)

Re: My review of the C standard library in practice

#30
post #21
post #19

Earlier quoted context omitted.

No, one should focus on creating smaller independent modules and don't mix unrelated stuff like networking fundamentals with string convenience functions.

Are you familiar with linker hell? I agree in theory with your suggestion, but not for C or C-like languages which have such a poor story for linking compilation units.

Well, the problem is not getting easier and having poor versioning has been hunting everyone for around as long as null references
Post reply on HN