←back to thread

228 points Retro_Dev | 2 comments | | HN request time: 0.51s | 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 #
gpderetta ◴[] No.44463597[source]
What the standard says about freestanding is all well and good. But what do actual embedded c++ compilers actually ship?

Also embedded covers a very wide range of computers.

replies(1): >>44463781 #
1. TuxSH ◴[] No.44463781[source]
If you are targeting armv4t/armv5/armv6k+vfp (or armv7 but not optimized for it) for Aarch32, or Armv8.0-A and are fine with newlib, then devkitARM and devkitA64, respectively, get the job done and ship GCC 15.1.

There is also devkitPPC, shipping with the same toolchain (and which additionally has some Obj-C support iirc).

Custom patches to newlib and consorts (https://github.com/devkitPro/buildscripts/) introduce hooks and WEAK functions that allow to implement standard library functions on almost any platform, on a platform library basis or even on a per-program basis (with some restrictions on lock sizes).

replies(1): >>44463922 #
2. gpderetta ◴[] No.44463922[source]
Indeed. My point was that freestanding is a strawman, with likely little relevance for embedded developers.