Hacker News new | ask | show | jobs
by kevin_thibedeau 1658 days ago
AVRs don't have enough storage for templated code to explode into an unmanageable problem.
4 comments

Somehow C64 can deal with them.

"CppCon 2016: Jason Turner “Rich Code for Tiny Computers: A Simple Commodore 64 Game in C++17”"

https://www.youtube.com/watch?v=zBkNBP00wJE

"C++20 For The Commodore 64"

https://www.youtube.com/watch?v=EIKAqcLxtT0

I love these videos, Jason is doing fantastic work with his YouTube channel and C++ Weekly series.
Check his CppCon 2021 presentation, done on a C64 emulator thanks constexpr. :)
Thanks, I will :)
But a C64 is still only talking about 64kB of RAM. The post you're replying to claims that Template complexity gets unreasonable on a large machine, such as a Linux system. Not sure I agree, but "it's fine on a C64" isn't evidence in your favour unless you've forgotten Linux doesn't even run on a Commodore 64.
So it is fine on a 64KB system, but unmanageable on a platform that gets all the different kinds of boilerplate to run cloud workloads, got it
C++ or even C isn't exactly "fine" on any 8-bit system though. It's nice for a little demo, it can even be tolerable for some real-world projects when mixed with large amounts of inline assembly, but those 8-bit ISAs have been designed mainly for manual assembly coding, not high level compiled languages like C.
Honestly unless you’re on something like an ATtiny with < 1K of RAM or doing cycle-counted stuff a properly adapted high-level language is fine. I mean, Forth (doesn’t have to but usually) uses an interpreted virtual machine and people have used and liked it on 6502s since those were the new hotness.

As far as I’ve seen, two things make C and C++ specifically problematic on 8-bitters: automatic promotion to int for all expressions, with int required to be at least 16 bits (a language problem); and subpar codegen on accumulator architectures and other things that are not like desktops (a compiler problem).

Forth is much better for creating tiny executables than C on 8-bit ISAs though, performance takes a hit because of all the calls/jmps, but it's still surprisingly good. C compilers on the other hand often create "obviously" dumb and inefficient code, at least in my experience (6502 may be better than Z80 in that regard though).
C translates directly to ASM in many cases. It just makes managing offsets and other stuff easier.

C++ adds type-safety on top of that for no cost. It's great when your compiler tells you that there is no operator =|(PORTD, PINA). Did you mean |=(PORTD,PIND) or =|(PORTA,PINA).

> C translates directly to ASM in many cases.

But usually much worse ASM than what a human would write on such CPUs, because the C compiler is still restricted by artificial high-level concepts like calling conventions, and it needs to wrestle with instruction sets that are not very compiler-friendly and tiny non-orthogonal register sets. C++ just adds a whole level of code obfuscation on top, so it's harder to tweak what code the compiler actually generates.

If you really want that in C, you can either use functions and wrap everything in (incompatible but internally identical) structs, or use Sparse and annotate those integer types to be incompatible. Not that you must prefer that to C++ (even if I do), just to note that you can make do with C if you want to.
I mean, apparently this is confusing, but yes, obviously.

If your Commodore 64 template is dealing with say, foozles that might be 8-byte, 12-byte or 16-byte, the complexity incurred is pretty small, bugs with foozle<16> are likely to be something mere mortals can understand and fix.

On a more complicated system like a cloud Linux setup the template may be for foozles that can be in any colorspace and on a remote machine or locally, and now sometimes the bug with foozle<HSV,remove> involves a fifty line error message because the compiler doesn't realise all that happened is you meant to write foozle<HSV,remote> ...

It's not even as if the C++ committee isn't aware that templates are a problem. Remember template meta-programming wasn't really intended from the outset, and a big part of the point of Concepts was to at last let you write code that compilers can provide readable errors for when it's wrong.

I understand that for C++ programmers "That's possible" versus "That's a good idea" is a distinction without a difference, however for the rest of us the fact you can use templates as much as you like in, say, Windows drivers, does not magically mean it's a good idea to write complex templated code in Windows drivers.

The constraints in /kernel like forbidding exceptions are because otherwise they (Microsoft) need to do a bunch of extra work to support your bad idea. But your use of templates has no impact on their work, so knock yourself out adding as much complexity as you like this way.

To be fair, DriverKit runs in userspace. IOKit, its spiritual predecessor, only allowed for the use of Embedded C++.
C++ templates are unwound at the compile time before the «expanded» template code passes along into the optimiser where most of the unused code is elided.

Unless the templates have been externalised (i.e. defined as «extern template …», of course). Even then, a modern compiler+linker combo will optimise most of the unused code away at the linking time thus reducing the final binary size. I do understand that the LTO might not be available for every embedded platform, though.

P.S. That is exactly the point of the C++ template metaprogramming – the hard lifting is delegated to the compiler, which leads to increased compile times but also to more efficient and very compact runtime code.

What do templates have to do with storage, though? My primary attraction to C++ templates is that they let me write very expressive code that will compile down to a handful of instructions. Now, actually compiling complex C++ templates on a storage-constrained system can be a problem, since templates are compile-time beasts, not runtime. Once compiled, though, they have a Cheshire-cat existence.

Edit: Unless you're doing something rather silly with the templates, but again, that's not a template problem.

The general complaint with templates is they are instantiated and if you’re not careful can bloat the binary with multiple versions of a piece of code. But this is usually something pretty easy to solve: just don’t do something that would cause that to happen :P
I have _never_ had any issue with C++ templates on _modern_ µControllers such as the ESP32. Unless you have an incredibly minuscule flash, modern GCC or LLVM are very good at deleting unused code when you compile everything with -Os. Even -Og isn't that critical either.