Live data from Hacker News

sp.h: Fixing C by giving it a high quality, ultra portable standard library

spader.zone

161–170 of 222 posts

Re: sp.h: Fixing C by giving it a high quality, ultra portable standard library

#161
post #62
post #3

My impression of the sample programs is that they're unreadably noisy, but maybe this would be a good compiler target if you're writing your own language?

How would you write https://github.com/tspader/sp/blob/main/example/ls.c in your statically typed language of choice? To be fair, this is definitely the kindest example to my library, but one reason I felt this project was worth pursuing was that that example reads basically like a slightly worse TypeScript to me. In other words, quite nice for how low level the code really is.

In a higher-level language, you wouldn't need to write code like this:

  const sp_fs_entry_t* a = (const sp_fs_entry_t*)pa;
  const sp_fs_entry_t* b = (const sp_fs_entry_t*)pb;
  return sp_str_compare_alphabetical(a->name, b->name);
Instead it would just be:

  return sp_str_compare_alphabetical(pa->name, pb->name)
With the correct types declared for the parameter types instead of void pointers.

Or if you do need a cast, not having to write "sp_fs_entry_t*" twice in the same line because the local variable's type is inferred.

Maybe after reading C for a while, you don't see all the noise anymore?

Re: sp.h: Fixing C by giving it a high quality, ultra portable standard library

#162

https://spader.zone/sp/#null-terminated-strings-are-the-devi... Pointer/length is not just for strings - but for all arrays. See my proposal: https://www.digitalmars.com/articles/C-biggest-mistake.html

But won't all those posix functions that take only `const char*` parameters need to be changed to be pointer/length?

No. For string literals, they already have a 0 appended, so no problem. For others, you'll need to malloc/copy/free.

It hasn't been much of an issue with decades of D code.

Re: sp.h: Fixing C by giving it a high quality, ultra portable standard library

#163

> Principles > Be extremely portable > sp.h is written in C99, and it compiles against any compiler and libc imaginable. It works on Linux, on Windows, on macOS. It works under a WASM host. It works in the browser. It works with MSVC, and MinGW, it works with or without libc, or with weird ones like Cosmopolitan. It works with the big compilers and it works with TCC. > And, best of all, it does all all of that becaus…

> Those are contradictory. Either the code is extremely portable, or it can't support "obscure" platforms, but not both.

I think it's perfectly valid to call code 'extremely portable' without supporting every special snowflake architecture. There's a spectrum from assumptions that hold on everything that isn't some esoteric joke architecture or archaeology to something that I would probably consider required for 'extremely portable'.

I would personally consider something that failed to support anything on this list above big endian as still being extremely portable: you'll build for any serious modern architecture that isn't a DSP.

  - non twos complement integers
  - (int) nullptr != 0
  - segmented addressing
  - non-8 bit char
  - big endian
  - missing floating point
ARM's done a good job of making it so that you can't assume the traditional x86 assumptions of being able to access any pointer unaligned or having sequentially consistent semantics on memory ordering (with the help of compilers getting better at reordering resulting in you needing to have proper semantics on x86 as well).

Re: sp.h: Fixing C by giving it a high quality, ultra portable standard library

#164
post #137

Earlier quoted context omitted.

It's an odd stance for a C library. In my experience, odd platforms are the main place that C is used in 2026. If you're writing a new Windows or Linux or MacOS or WASM program, it's not likely to be in C. But lots of new microcontroller software is still being written in C. And he's already hit the hard targets. Many obscure OS's are generally UNIX like and should be easy ports. Many obscure arch's usually are runni…

Aren't these MCUs predominantly ARM-based, with some RISC-V thrown in? I see no contradiction in the desire to support x64 (because it would be ridiculous not to), ARM, and likely RISC-V, but not the venerable but now-fringe architectures like MIPS or Sparc or 68040 or even x86.

The author can do whatever they want. But if all you want to support is x86-64, ARM64 and maybe some version of RISC-V, don't crow about how 'extremely' portable you are. At best, you don't know.

Re: sp.h: Fixing C by giving it a high quality, ultra portable standard library

#165

> Principles > Be extremely portable > sp.h is written in C99, and it compiles against any compiler and libc imaginable. It works on Linux, on Windows, on macOS. It works under a WASM host. It works in the browser. It works with MSVC, and MinGW, it works with or without libc, or with weird ones like Cosmopolitan. It works with the big compilers and it works with TCC. > And, best of all, it does all all of that becaus…

> Those are contradictory. Either the code is extremely portable, or it can't support "obscure" platforms, but not both. I think it's perfectly valid to call code 'extremely portable' without supporting every special snowflake architecture. There's a spectrum from assumptions that hold on everything that isn't some esoteric joke architecture or archaeology to something that I would probably consider required for 'ext…

It makes liberal use of u64 all over the place rather than a more appropriate, machine adaptive unsigned int or unsigned long. It isn't a good fit for anything "exotic" like non-64-bit platforms. I wouldn't consider that in the spirit of portability when it compiles into bloated code with unnecessarily large structs.

Re: sp.h: Fixing C by giving it a high quality, ultra portable standard library

#166
post #80

First, thanks for sharing this link, it was an interesting read! A few remarks below. I had a hard time reading the wc code in the article. First I had to go to the GitHub to understand that "da" stands for dynamic array, and then understand that what the author calls wc is not at all the wc linux commands, which by default gives you the number of lines, words, and characters in a file, not the count of occurrences o…

To be portable it's probably best to use the pthreads API for everything, make no additional assumptions, and rely on the user to provide the implementation. Consider what happens when someone is working with OpenMP, CUDA, or similar and attempts to make use of a dependency that in turn makes use of your library. The easier it is to understand the assumptions made by your library the better.

Re: sp.h: Fixing C by giving it a high quality, ultra portable standard library

#167
post #62

Earlier quoted context omitted.

How would you write https://github.com/tspader/sp/blob/main/example/ls.c in your statically typed language of choice? To be fair, this is definitely the kindest example to my library, but one reason I felt this project was worth pursuing was that that example reads basically like a slightly worse TypeScript to me. In other words, quite nice for how low level the code really is.

In a higher-level language, you wouldn't need to write code like this: const sp_fs_entry_t* a = (const sp_fs_entry_t*)pa; const sp_fs_entry_t* b = (const sp_fs_entry_t*)pb; return sp_str_compare_alphabetical(a->name, b->name); Instead it would just be: return sp_str_compare_alphabetical(pa->name, pb->name) With the correct types declared for the parameter types instead of void pointers. Or if you do need a cast, not…

Because it's const void *pa, you don't need the cast. A void * pointer will convert to any other kind of pointer. Now you only need to mention the type once. (I forget the const-related rules, but since the consts match in this case I don't think it'll be relevant.)

Re: sp.h: Fixing C by giving it a high quality, ultra portable standard library

#168

Earlier quoted context omitted.

Is the Foo kernel 75% of the code base, 5% or 0.01%?

I’m not getting dragged into a strawman argument about meaningless hypothetical percentages of a vague and arbitrary illustration. And particularly not when the point being made was pretty clear: > “Portable”, in the context of how it was used, generally refers to software using platform agnostic idioms.

Portable has two meanings: a construct is portable if we can rely on it to work everywhere. A portable codebase is one that supports moving to a new platform, with some nonzero effort which is not large compared to rewriting the code. It is able to be ported. E.g, "Johnson's Portable C Compiler (PCC)".

If only the Foo kernel must be rewritten in order to port Foo, but that kernel is 75% of Foo, then I would say Foo is not portable. If the kernel is 0.1% of Foo, then I would say that it is: 99.9% of the code base depends on the abstractions in the Foo kernel rather than platform features.

Re: sp.h: Fixing C by giving it a high quality, ultra portable standard library

#169
post #72

Just taking a quick look at the atomics section: First, (on unix) it's wrapping pthread mutex. That's part of libc! (Technically it might not be libc.so, but it's still the standard library.) Also, none of the atomics talk about the memory model. You don't _have_ to use the C11 memory model (Linux, for example, doesn't). But if you're not using the C11 memory model and letting the compiler insert fences for you, you…

Yes, unfortunately the threading primitives require libc. Ditto subprocesses. It's on my list. But regarding: "Oops... apparently this is vibecoded. Welp, I just wasted ten minutes of my life reviewing slop that I'm not going to get back." Do not talk to people like this. I don't care if you don't like the library, or if you found a flaw in it. I am a regular person who wrote this code for no other reason than I thou…

[deleted]

Re: sp.h: Fixing C by giving it a high quality, ultra portable standard library

#170
post #140
post #86

Earlier quoted context omitted.

One I can think of is simplicity. No need to worry about what the type of the string should be (size_t?) or where it should be stored. Just pass around a pointer. Pointers fit the size of a CPU register most of the time. Though in my opinion the drawbacks (O(N) performance, NUL forbidden etc.) outweigh this benefit we are stuck. Many kernel interfaces like open, getdents etc. assume NUL-terminated strings, therefore…

But (i32 length, byte[] data) is as complex as (byte[] data, '\0'), its two-parts anyway. Of course it allows potentially for very long strings at the cost of just a single byte spent as a terminator. Beside the rarity of such a case, the "space savings" might play a role on a PDP11, or on a Z80, but not on any of the modern architectures that need structures aligned to 32 or even 64 bit boundary. The efficiency and…

Arrays as glorified pointers were the mistake. Null terminated strings are a natural result of that design choice.

Null pointers however were not a mistake, despite how popular slandering them has become. A reasonable case can be made that any modern language should enforce null checks (and bound checks, and ...) or at the least provide them by default but that is neither here nor there as far as C is concerned.

Post reply on HN