bitcicle


How I do my computing

If you'd like to know about how and why I run my computers, grab a cup of coffee and maybe a bucket of popcorn and be prepared to read some wild stuff. No just kidding, this is how I do my computing!


I do the best I can to use as much open source and/or free software as possible, so I use Linux on all of my computers. My desktop computer usually runs a rolling-release distro, which at the moment is Arch Linux, although I used Void Linux up until a few months ago. My laptop on the other hand runs Debian Testing, because it gives me a great mix between stability and up-to-date packages. And yes, I absolutey type apt by accident on my desktop and pacman on my laptop.

On both of my primary computers, I tend to run tiling window managers. These make the most use out of my screen real estate and allow me to navigate seamlessly without taking my hands off of the keyboard. I tend to prefer terminal based programs to those with a UI because they're less bloated and are more easily scriptable for automation. For code/text editing, I use Neovim for pretty much everything. If you'd like to see how my system is configured, take a look at my dotfiles.

The programming languages I use the most are C and modern C++, although I've been exploring more "modern" languages lately.

C was the first programming language I learned, which was in 7th grade. I still primarily use C for very resource limited embedded systems, or working with low level device drivers. In my opinion, I think C is the most elegant programming language. I know, I know. I'm crazy. C is just delightfully simple and easy to read. Nothing hides in the background, and everything you see is everything that happens (besides a few string functions like printf and the like). It doesn't hold your hand or force you into certain programming schemes, that's all up to you. While yes, this makes it a much more error-prone language, it also makes you a much better programmer. That being said, to program in C I have to put myself into a certain mindset and make sure I'm extra careful when playing with memory. Nowadays however, I tend to lean towards modern C++ as a replacement for C.

Compared to many people, I find modern C++ very safe and efficient, as long as you stay away from raw memory access. I wish the C++ committee would man up and drop backwards compatability with C. At this point, everyone who complains about C++ being unsafe still uses C++98 (or maybe C++11) and doesn't take advantage of the language's newer, safer features, just the backwards compatable raw pointers provided by the C compiler. Granted, some newer features, I'm looking at you coroutines, are abysmal and cause more problems than they solve.
Generally, my rule for C++ on embedded systems is the following: No RTTI, no exceptions, and avoid new/delete (sadly this means a lot of std:: goes away). This is generally okay because I tend to create static, templated containers to match that of the standard library. A goal of mine is also to make as much of my code compile-time executable as possible. This is not only safer, but more efficient during runtime. Mind you, while I love modern C++ and find it safe, that's because I use it safely. A lot of the "safety" features can be easily bypassed and aren't compiler enforced. "Safe" containers such as std::optional are great, but the fact that they allow exceptions to be thrown defeats the entire purpose. Other languages, such as Rust, defeat this issue by requiring a stored value check in Option. This is ideal, but C++ is too far gone to use this style now.

There are a variety of other programming languages I use and/or learning. I've started learning similar C-like languages such as Rust and Zig. The idea of these languages are great because they give the same level of control as C/C++, but are incredibly safe and don't let you make simple mistakes. So far, I really like Rust, however there are some things that I'm not a huge fan of. The "forced" code formatting in snake_case is silly, and the syntax is pretty nasty in my opinion. That being said, being required to register error handling on function calls is fantastic and makes code nearly bulletproof to careless laziness, because it won't let you be. It's also incredibly hard to shoot yourself in the foot. Fantastic!
One thing I wish Rust would implement, especially for embedded systems, is the forced declaration of an allocator for all classes and/or functions that allocate memory. This is something implemented in Zig that makes a ton of sense because, 1: It lets you know when memory is being allocated without any guesswork, and 2: Custom allocators can be used to avoid turning the heap into swiss cheese with custom allocation and cleanup methods tuned for the required usecase. Update: Since I've written this I've played with custom allocator definitions, and even the heapless collections that Rust offers. I must say that they do wonders for embedded systems. Big thumbs up from me!

For more recent projects, especially those targeting desktop applications, I've been getting really into scripting my code with Lua. Having run-time scripting adds so much value and usability to a program. It can be easily modified for specific applications without needing to restructure the entire core of the application. Plus, Lua's C API is stupid easy to use, and actually executes pretty quickly, especially with LuaJIT.

When it comes to debugging, I always use GDB and Valgrind. No matter what project I'm doing, I make sure I'm not creating any memory leaks. Lately for my Rust projects though, I don't have to worry about that as much :). I use GDB to debug pretty much everything, including embedded systems. I've used a few GDB interfaces and frontends, but they never end up being as good at GDB itself, so I tend to just use the CLI interface.

Homelab

Homelabs are awesome!! I run a homelab on my network for a couple reasons, but the main reason is because I love doing it! I self-host tons of services to avoid the use of big data companies like Google and Apple, and I use my homelab as a place to learn! As well as providing services for myself, I host video game servers for friends and friends of friends so we all have a place to play our games for free that's customizable exactly how we want it.

I run all of my services through Proxmox rigs because I love the security that comes with sandboxing all of my apps, but also the ease of management it provides. All of my servers are clustered into a single datacenter, "MiddleEarth". As a side note, I love Lord of the Rings, and all of my devices are named after people and places from Middle Earth.

My datacenter, MiddleEarth, is set up as follows:

Diagram:

Diagram coming soon.

Upgrades: