2025-09-20

I don't want a framework, but I do want the convenience

Programming frameworks can be a huge boon in terms of simplifying the provision on desirable features and behavior that aren't the core of your project.1 But they evolve toward someone's ideal of an all-singing, all-dancing, universal tool and that makes them huge and complicated. For a lot of the things I write at home, I don't need most of those facilities and I don't want huge or complicated. But I also don't want to be writing thousands of lines of code just to make my tool ergonomic.

Thankfully the open source community provides hundreds of high quality utility libraries covering all kinds of tasks you might want to do. Usually and handful or more for every feature you might want. Which means you have to choose.

This week, I decided that I wanted a command-line argument processor for a tool I"m dinking on. I've used gengetopt in the past when simple ad hoceries were insufficient, but it needs a two stage build and has a clunky, pure-C interface. Surely I can do better than that!

How to decide?

Well, I have a partial answer: The build system I use for home projects is Meson, which uses a facility it calls "wraps" to support fetching and building projects you depend on.2 Moreover, the tool has a facility to fetching wrap files from a centralized database which makes adding a provisioning library to your project nearly transparent.

So I'm going to restrict my consideration to tools that appear on Awesome C++ and are available from Meson's Wrap DB. There might be other good options out there, but these have had some level of peer review and (critically) are easy for me to get. That gets me down to:

After a cursory reading of the github READMEs it's clear that they all provide the features I need and have a version of the same kind of interface that QT uses.3

But that doesn't mean they're equivalent. One (argparse) uses a Method Chaining (AKA Fluent) interface for the "tell the parse what arguments to expect" step,4 and another (cxxopts) uses method chaining with statically polymorphic calls to operator() which makes a slightly weird syntax that I suspect clang-format is going to balk on. Neither of those is a deal breaker, but they are a little odd. Alas, the third option (CLI11) is a little clunky: requiring you to create two objects and relying on a try-catch for detecting errors (though it provides a c-preprocessor macro to hide the exception handling if you prefer).

Anyone have any relevant experience here?


1They also provide consistency across a set of tools for both users and developers, and you should not neglect that value in that. At work we use Qt, which has all the faults I list above and a swath of legacy issues as well. But the bit where my manager can say "Hey, we like that command line feature you added on your project, put it in Jane's too." And even though I've never worked in Jane's project before I can still dive right in because I know how command-line interactions work in QT. Programming at scale is a team sport and that changes the trade-offs.

2 I already use the doctest wrap in several projects, so I know how this goes.

3 You create a parser object, tell it what arguments you expect, pass it the command line, and then ask it what it found. With convenience facilities for detecting violated expectations issuing help messages, and so on.

4 I've tried method chaining a few times in my own coding, and found that I only like it in cases where most of the uses of a class are going to be transient. Thankfully, if you declare a named object you don't have to use the fluent syntax if you don't want it.

2025-09-07

Wait, how many nodes?!?

My employer has been working through a IT security upgrade process, and recently performed a dry-run of an upcoming external audit. They report that they didn't find anything surprising or particularly worrying, but they took the opportunity to roll out new and updated employee agreements for just about everything computing related. Use of company network resources and email addresses, protection of company hardware, and so on. Including a new agreement for work-from-home.

That WFH agreement is the one that caught my eye. Buried in a lengthy bulleted list was a requirement to have anti-virus on "every other system on the network". We have—currently—one windows box, one antiquated mac, a networked Epson printer, a few linux systems, three phones, an e-book reader and a bushel (or at least a peck) of tablets. How does that mess fit in?

I run Clam AV on all the Linux devices (I have reason to think that will be considered sufficient). My wife's windows system has Windows defender, and I'm told that's okay. But what about the antique Mac (even if I only boot it occasionally), and those tablets, phones, and readers? And what about guests: we have a bunch of people who come to visit bringing phones, computers and tablets with them, and common hospitality requires that we offer them connectivity.

Then, as I started to think about it, I realized that this list was incomplete. Like, seriously, incomplete. I mean, the PlayStation 4 is just the beginning. Despite an disinterest in "Smart Home" stuff, we have a number of things that could reasonably be characterized as IoT devices.

So I launched into the hardware identification stage of a network security audit this morning.

We have two smart TVs; two other WiFI equipped appliances (though we have not configured their WiFi connections), and at least ten other WiFi connected nodes providing minor but useful services. That's circa thirty devices sharing a single network segment. And it got that way one, little choice at a time, without us really noticing.

My current thinking is that I need to segment the network to isolate guest and IoT devices from our primary computing resources and run at least some basic intrusion detection. Fun, eh?

2025-08-27

The problem with the solution

Out-of-source build are a solution to a number of problems. Most pressing in my day to day work they make it efficient to maintain separate builds and jump back and forth between them; they reduce the frequency of committing things to the repository that have no business being there; and they make it possible to play nice with IT (source is backed up, build is not). As such, they are widely recommended by respected professionals (and I see few serious attempts to defined in-source builds), but they are not without their own problems.

The particular problem that I want to talk about today is build metadata. My work projects use CMake,1 which has two implications:

  1. Once configured the build system stores all the metadata in ${BUILD_DIR}/CMakeChache
  2. But you occasionally (::cough:: pretty regularly) have to blow the cache away and start over

Worse, for design reasons driven by a core dependency, the build of my main project needs a pretty complicated set of environment variables to configure.

No problem, though, the IDE will store that stuff for you, right?

Yes. Yes, it will. In a tool-specific file. In the build directory.

And yeah, you can .gitignore the IDE files so they don't get committed, but because each IDE has it's own name, that's a little catalog of names to maintain. Similarly, you can't reuse information cached by one tool in another. It's be nice to have a unified standard for common things (build locations, environment variables, etc) with some provisions for tool-specific extras.


1 I know, I know. Don't get me started.

2025-06-17

An orange ring for git submodules

At work we have a common library, call it libThing, that underlies several of the products we produce for one of our big customers. We bring it into the projects that use it with git submodules. And that's a bit of a problem.

Let me explain.

You see, the design of the submodules facility is clearly predicated on an understanding that the submodule is a separate thing, and is not edited in situ by a programmer working on the super-project. If the sub-project needs updating, it is assumed, you will send the maintainer a well defined change request, wait for it to happen, and point your super-project at the updated version and go about your business.

Which is what you would expect if the sub-project belongs to someone else.

And some of our changes look like that: "Folks, we have a note from the customer. There's a new file format for specifying widgets. Someone needs to update the WidgetLoader in libThing to handle it. Joe, you've worked in that module recently, can you get to it this week?". Fine. Joe updates libThing, pushes to the reference repository and the next release of each of our project can manage the new Widget files. Nice. And exactly as Linus envisioned it.

On the other hand a lot of times we find out that libThing needs updates because we're in the course of making changes to one of the project that use it. By working both side together we can work through the trade offs dynamically. Its more natural, and probably faster, to just work on them together. Even though submodules doesn't encourage it.

Buuuut ... if you're not careful, you'll make one or more commits to the sub-project in a detached head state.1

The rest of this post is a recipe getting safely back to a happy state after you make this mistake.

Advancing the branch your aren't on

If you had done this right, you'd have put the subproject on the branch before you started editing and the branch would have advanced as you made commits. We want to get the repository into the state it would have had.

  1. Determine the name of the branch you're suppose to be on by looking in the .gitmodules file of the super-project. Remember that for later.
  2. Give the current state of your work in the submodule a temporary name with git checkout -b life-preserver2
  3. Get on the right branch with a git checkout to the branch you found in step 1.
  4. Fast-forward with git merge life-preserver (and it should be a fast-forward merge; if not you should probably take stock at this point.)
  5. Check that nothing is missing by examining the commit tree. I like gitk --all.
  6. Assuming all is well, dispose of the evidence with git branch --delete life-preserver.
  7. Detach the head again (at least if you're done) git checkout --detach HEAD.
  8. Pretend you're the kind of coder who never misses the little details.

1 If you don't work with git, then this post probably isn't much use to you, but short-short version:

  • In git a "branch" is a name that refers to some remembered state of the project. A project can have a lot of branches, and you can start with one state and make different changes to the project and have both of the remembered as different branches. That is, they can split off like the, well, branches of a tree.* They can also join up which is called a "merge".
  • You can tell git to remember a new state of the project. That's called a making a "commit" and each commit knows about the one(s) it came from, so the software can navigate back in time.
  • A commit can be the target of zero, one, or more branches.
  • You can be "on a branch" meaning that (a) you are working on the state of the project remembered by that branch name and (b) git has a record of which branch you're "on".
  • When you're on a branch and you make a commit, git changes the association of branch name to the new commit. Remember that commits know what came before, so you can still go back, but the name now refers to the new state of the project.
  • If you're not on a branch you are in a "detached head state" which means git knows which version of the project you start with but doesn't know a branch. Any commits you make in this state are nameless because there is no branch name to move forward.
  • * In computerese it's actually a directed acyclic graph (DAG), but that's neither here nor there.

2 Yes, I have a name I use for this. Not that it happens often or anything.

2025-05-14

Getting to do grown-up things

We took the seven year-old to an art museum yesterday so that Mommy and Daddy could see a traveling exhibit focused on a single artist.

And not only was it not a disaster, after a while she disengaged from the iPad and took an active interest in the art. She has a favorite among the pieces we saw.1 She remarked on how interesting it was to see the studies the painter did toward another large canvas.

Victory!

Well, at least a conditional win. We didn't stay to see any of the permanent exhibits because she was clearly done at that point. But still.


1 It's one of my top ten, too!

2025-05-13

Maybe another example of my favorite weakness of LLMs?

We've flown out to stay with my in-laws for the best part of a week. Because reasons.

I brought my work computer in case I need to respond to some crises or just find some time to do useful work rather than burning PTO (which is always in short supply). Unfortunately, my work computer is currently an eight-pound, desktop-replacement beast. For which I really want to bring a spare display (and I have fifteen-inch, USB HD panel for that purpose). With all the weight and volume devoted to work computing I opted for the barest minimum of person hardware: my Raspberry Pi 400, its mouse and, associated cabling to talk to that same panel.

Now, the 400 is a surprisingly good computer for what I paid for it, but it's also quite limited. In particular it has only 4GB of RAM, and while the CPU supports NEON SIMD there is no dedicated graphics processor. It's completely unsuited to running LLMs without add-on hardware. But I got bored, so I decided to try anyway.

I was looking for relatively modern models that will fit in RAM and found Llama3.2:1b (7 months old) and codegemma:2B (9 months old). One conversation and one code support. Nice.

I've been speculating about down-quantizing some slightly bigger models, but in the mean time I started playing around with the baby llama. I didn't want to challenge it with my usual questions for probing the weakness of larger and more powerful models, so I started by just asking it to tell me about the band Rush.

As models do, it then produced a bunch of plausible sounding text. Some of it was incomplete and some was simply wrong. For some reason it thinks Rush won more major award than they did. It seems to have selected exclusively praising text to reproduce (which is fine by me: I like the band and don't find most complaints directed at them to be at all convincing), but there is a non-trivial amount of criticism and none of it is represented or even acknowledged by the answer.

Now, let's be frank: there is a limit to how much factual detail you can expect to be encoded in what is, after all, a somewhat niche cultural subject when you have only two billion parameters available to try to represent a full cross section of English language knowledge. No general purpose model of this size is going to get everything right on a query of that kind. So, I'm not saying that the fact of errors is surprising or even interesting. But I am wondering what we can learn from the nature of the errors.

So let's look more closely at some of the errors. In its "Discography" section the model lists

  • Start with School Days (1974)
  • Caress of Steel (1975)
  • Fly by Night (1975)
  • 2112 (1976)
  • Two-Side Streets (1978)
  • Permanent Waves (1980)
  • Moving Pictures (1981)
  • Hold Your Fire (1987)
  • Presto (1997)
  • Test for Echo (2004)
  • Snakes & Arrows (2007)
  • Clockwork Angels (2012)

which has both omissions and errors.

I haven't the faintest clue what to make of the title mistake for the first album (which was 1974 but was self-titled). It's not the title of any Rush song or album that I'm aware of and a casual search of the web doesn't turn up any song or album by that name at all. The search does turn up a reasonable number of hits on Stanley Clarke's 1976 album School Days and a number of suggestions that people just beginning to explore Mr. Clarke's music should "Start with" that very same album. Interesting, but not terribly enlightening.

Nor do I have any theories concerning which albums are omitted. It doesn't look to me like the list is either the commercial hits or the fan favorites, and beyond that I don't know what patterns to look for.

But I do want to talk about the 1978 entry in the model's list. The proper album title for that year is Hemispheres.The thing that strikes me here is proper title and the substitute text share a conceptual relationship to the number two (two halves; two ways). My (admittedly wild) guess is that we're seeing a side affect of the model's attention system identifying "two" as an important concept to use when trying to infer tokens.

If true that would be interesting, because the attention system is one of the significant ways in which LLMs differ from Markov Chain generators. But it may also be responsible for the models difficulty in know what is a quotation and what is commentary which I've already discussed in the context of scientific papers.

2025-04-27

Why did all the numbers move to the back of my credit card?

One of my credit cards has a feature that baffles me: all the numbers are on the the same side1 of the card.

Now, credit cards present an interesting trade-off problem between security and convenience. Bruce Schneier spends some time on the matter in one of his books. Maybe Liars and Outliers. A certain level of loss to fraud is accepted to ensure that the system is convenient and ubiquitous; and the history of the technology is an epic tale of continual re-tuning of the risks.

In the very early days the system was surprisingly simply, relying on people and paper. Really. By the time I came on the scene, the cards had raised numbers on the front to impress multiple copies of the transaction record on carbon papers. The "chunk, chunk" of the cashier making the impression was the sound track of eighties retail. Later we got smart cards and now touchless payment.2

Hey, Ma! Look at me! I'm living in the future!

And somewhere along there (late '90s) the "card security code" was added to the back. It didn't show up on the mechanical impressions (still in use then in the US, if not in really advanced parts of the world) or in a single-sided xerox of the card, so it made it slightly harder for bad actors to capture enough information to create fraudulent charges. Not really hard, mind you. Just hard enough. That's one of the surprising things about this story.

But now I have this card that has the CSC printed right next to the main card number and expiration date.3 Huh? Is that Okay because merchants are now using billing zip code as an additional (if very weak) authenticator? Or is it something else?


1 The side with the magnetic strip and opposite the conductive pads which I would describe as the back, but it's the fact that they're all on the same side that bothers me.

2 The touchless systems marks one of the first moments I started to feel like technology was leaving me behind. Another unanticipated milestone.

3 It also has the main numbers printed flush on the surface of the card: they're not raised.