←back to thread

228 points Retro_Dev | 1 comments | | HN request time: 0.247s | source
Show context
blippage ◴[] No.44462083[source]
I tried Zig some time ago to use with microcontrollers. It has a generator for the pins, which was nice. But subsequent versions broke as Zig changed syntax. So I started going down the rabbit-hole (it needed a newer version of llvm, for example) until I eventually decided that the game wasn't worth the candle.

The fact that another breaking change has been introduced confirms my suspicion that Zig is not ready for primetime.

My conclusion is to just use C. For low-level programming it's very hard to improve on C. There is not likely to be any killer feature that some other contender will allow you to write the same code in a fifth of the lines nor make the code any more understandable.

Yes, C may have its quirky behaviour that people gnash their teeth over. But ultimately, it's not that bad.

If you want to use a better C, use C++. C++ is perfectly fine for using with microcontrollers, for example. Now get back to work!

replies(10): >>44462098 #>>44462163 #>>44462194 #>>44462322 #>>44462409 #>>44462430 #>>44462780 #>>44463127 #>>44463517 #>>44464834 #
juliangmp ◴[] No.44462409[source]
Obligatory C is not a low level language: https://queue.acm.org/detail.cfm?id=3212479

I also have to disagree with C++ for micro controllers / bare metal programming. You don't get the standard library so you're missing out on most features that make C++ worthwhile over C. Sure you get namespaces, constexpr and templates but without any standard types you'll have to build a lot on your own just to start out with.

I recently switched to Rust for a bare metal project and while its not perfect I get a lot more "high level" features than with C or C++.

replies(4): >>44462438 #>>44462543 #>>44462692 #>>44464014 #
TuxSH ◴[] No.44462543[source]
> You don't get the standard library

Why is that? Sure, allocating containers and other exception-throwing facilities are a no-go but the stdlib still contains a lot of useful and usable stuff like <type_traits>, <utility>, <source_location>, <bit>, <optional>, <coroutine> [1] and so on

[1] yes they allocate, but operator new can easily be overridden for the promise class and can get the coro function arguments forwarded to it. For example if coro function takes a "Foo &foo", you can have operator new return foo.m_Buffer (and -fno-exceptions gets rid of unwinding code gen)

replies(2): >>44462574 #>>44463081 #
tialaramex ◴[] No.44463081[source]
In the C and C++ languages there's a thing called a "freestanding" implementation. This is roughly analogous to Rust's nostd.

In C the freestanding environment doesn't provide any concrete features, you don't get any functions at all, you can get a bunch of useful constants such as the value of Pi or the maximum value that will fit in an unsigned integer, some typedefs, that's about it. Concrete stuff from the "C standard library" is not available, for example it does not provide any sort of in-place sort algorithm, or a way to compare whether two things are the same (if they fit in a primitive you can use the equality operator)

In C++ there are concrete functions provided by the language standard in freestanding mode. These, together with definitions for types etc. form the freestanding version of the "standard library" in C++. There's a long period where this was basically untended, it wasn't removed but it also wasn't tracking new features or feedback. In the last few C++ versions that improved, but even if you have a new enough compiler and it's fully compliant (most are not) there's still not always a rhyme or reason to what is or is not available.

In Rust it's really easy. You always have core, if you've got a heap allocator of some sort you can have alloc, and if there's a whole operating system it provides std.

In most cases a whole type lives entirely in one of those modules, Duration for example lives in core. Maybe your $5 device has no idea which year this is, let alone day but it does definitely know 60 seconds is a minute.

But in some cases modules extend a type. For example arrays exist in core of course - an array of sixty Doodads where Doodads claim to be Totally Ordered, can just be unstably sorted, that works. But, what if we want a stable sort, so that if two equal Doodads were arranged A, B they are not reversed B, A ? Well Rust's core module doesn't provide a stable sort, the stable sort provided uses an allocation and so the entire function you need just doesn't exist unless you've got allocators.

replies(2): >>44463597 #>>44463716 #
1. TuxSH ◴[] No.44463716[source]
I know how freestanding works, and I agree that Rust's "nostd" is much more thought out than C/C++'s freestanding, however

> This is roughly analogous to Rust's nostd.

"freestanding" is actually worse that this. It means that the compiler can't even assume things about memcpy and optimize it out (as on gcc it implies -fno-builtin), which pessimizes a lot of idiomatic code (eg. serialization).

The "-nostdlib" option is usually what one wants in many cases (don't link against libc but still provide standard C and C++ headers), such as when compiling privileged code with -mgeneral-regs only and such. This way you can benefit from <chrono>, etc.

If you are writing userland code you should be using a toolchain for this, instead of relying of freestanding/nostdlib which are geared towards kernel code and towards working around defective toolchains.