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.
My review of the C standard library in practice
21–30 of 94 posts
Re: My review of the C standard library in practice
#22Earlier 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.
“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
#23Earlier 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.
Re: My review of the C standard library in practice
#24Earlier 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.
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
#25Re: My review of the C standard library in practice
#26Earlier 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.
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
#27Earlier 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?
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
#28Earlier 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…
Re: My review of the C standard library in practice
#29When 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".
Re: My review of the C standard library in practice
#30Earlier 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.