2022-11-07

Not an advertised feature

The child has a tablet and likes to stream stuff on it in the car, which means tethering it to one of our phones (yes, we're wondering about making the next one cellular enabled). You can imagine the various minor inconveniences that implies, but it also means that when my wife set off on the pre-school delivery run leaving her phone in the upstairs bathroom (where the tablet had no trouble connecting to it), she rapidly learned that something was wrong.

Back they came and (using her smartwatch) she called me from the driveway to tell me she didn't have her phone. Cue double take.

New use for the table: phone abandonment detector.

2022-11-01

Merge tool supporting on-the-fly revision history?

There is this internal library where we've been working on four branches. Call them devel, feature-a, feature-b, and feature-c. All three feature branches are significant and long running, but they have at least had the incremental improvements and bug fixes from devel regularly merged back in.

Recently feature-a and feature-b were merged to devel (by someone else, yeah!). Which means that devel now has large changes relative feature-c. Now, I'm one of the folks working on feature-c and I spent a little while today trying a "what-if" merge of devel into that branch. It wasn't as bad as I feared (because much of this branch is working on different parts of the code than the others), but it was bad.

While I was staring at the merge tool showing another set of conflicting changes without (a) recognising either change as one I had done or (b) understanding the intent behind either occured to me that it would be really nice to be able to ...

Frob1 a highlighted (i.e. changed) section of text to obtain a quick peak at the relevant change logs.

In the simplest form you would see the change summaries from the current commit back to the common anscenstor for each branch; better would be filtering only those commits that touched the current file, and best would be filtering those that involve the lines in question. Being able to drill down on interesting summaries is a bonus feature.

At that point—assuming your team writes reasonable commit messages—you have a fighting chance of sussing out the intent of the changes and thereby a better chance of choosing the right merge action. Of course, I can (and do) check those logs in a couple of terminals kept handy, but UI sugar can improve the experience.

This seemed like something someone might already have implemented, but my Google-fu wasn't up to finding it (or reliably eleminating the possibility). Anyone know?


1 Hover over, center click on, or something. Ask a UI specialist to help.

2022-10-22

This is not a job for the algorithms header, after all.

As Mark points out below, the whole premise of this post is based on having misread the documentation. My initial expectations were more or less correct, and my effort to be a careful documentation-reading programmer back fired. Well, $#!+! There is a probably a lesson there.

Watch this spot for further posts to push my shame down past the point where most poeple keep scrolling. Or something.


I was stymied today.

You see the code I was working on feaures a type, call it KeyThing, that includes a double weight member intended to be used in forming averages over some information accessible using the rest of KeyThing.

Because the absolute scale of the data accessed wih the key matters, averaging is proceeded by a normalizing step: the sum of the weights must add up to unity. Which means you have to, well, accumulate the weights. Right?

So, being a good little programmer, I reach for the algorithm header and start by writing const auto sum = std::accumulate(collecion.begin(), collection.end(), 0.0, [](){}); only to pause briefly because I need to give the empty lambda the right signature. I'm thinking that it must take one double and one iterator that I can use to get access to the weight, and I just need to look up the order they come in.

C++ Reference quickly disabused me of that notion. The binary operation in std::accumulate expects to recieve two values that are of (or are implicily converable to) the type of the initializing argument (here double).

I can't use a lambda like [](const container::const_iterator it, double sum) { return sum + it->value; } wih either order for the arguments. Perhaps the symmeric-arguments version is more in keeping with the principle of least astonishment and generates clearer code in the easy majoriy of cases, but this is also a lost opportunity for more generaltiy. Now, obviousness and clarity are significant virtues in an API, so I won't claim it represents a bug, but it sure tripped me up this time.

So I wrote double sum = 0.0; for (const auto & key : container ) { sum += key.weight; } and moved on. Because while I could write nsc::accumulate which uses my version of the signaure that would be less clear.

2022-10-13

On being "that guy"

I contacted a support desk with an issue that I should have had instructions for but didn't. So far so good.

However, I followed up by asking a question about their response that I'd have answered for myself if only I'd scrolled beyond the page break. Not a good look.

In my defense there was a huge blob of whitespace at the botom of the page and I thought I'd reached the end of the document.1

Alas, after they poliely didn't tell me what an idiot I was being, I commited the "didn't read far enough" error again. Sigh.

Sorry guys. I did frontline support for a while and I feel for 'ya.


1 Because Word is paricularly crappy at layout even by word processor standards. One of the things I miss about academia is LaTeX: the typesetting and layout might be boring but they will avoid the easy misakes by default.

2022-09-19

Sworn enemies?

Even the pious Scots, locked throughout history in a long-drawn-out battle with their arch-enemies the Scots, managed a few burnings to while away the long winter evenings.
Terry Prattchet

The last couple of weeks have seen me drawn into another skirmish in the defining contest of the information-age workplace: schedule and cost estimation for development projects.

Managers of course need to know what things will cost and when they will be done. Developers know from hard won experience that it is not possible to know either of those things before you start the work. The closest things to reliable soluions that have been developed seem to fall into two rough categories:

  • Process-heavy mechanisms where you front-load the detailed design and get a moderately reliable estimate of the total cost and time when you're 15-20% into the effort, but entailing some risk of a complete failure
  • Agile methods which get to something early and evolve ever closer to the desired product over time and might be alleged to generate a reasonable approximation to the spec given the schedule and budget allow

Now, our client has allowed us to pursure the latter strategy while my project was an exploration of possibiliies (and just as well, it's what I know how to do), but now that it is a product in actual use, they are asking for or even insisting on firm commitments on both time and money. Sigh. Best guess and a generous pad it is.

2022-09-09

Algorithms of adjacency

Today at work I wanted to test the relationships between the contents of a vector in a sequential manner: cell i+1 should have this relationship to cell i kind of thing.

Question:
Does the algorithm header support that?
Answer:
Yes, two ways.
Question:
Can you tell just by reading a list of the calls?
Answer:
Maybe. How much attention are you paying to the signatures?

The key word here is "adjacent". Two of the algorithm header routines have that word in their name: adjacent_find and adjacent_differernce, and both of them have an overload where you supply a binary operator.1

Of course, the issue of difficult naming rears its ugly head here: adjacent_difference can perform any computation it wants to create the output values written (i.e. it is a version of the pairwise overloads of transform specialized for adjacent cells). Kate Gregory's story about partial_sort_copy is relevant here.

So that's cool: what I wanted was right there.

Only I started thinking2 about the other things I might want to do this way, and I noticed that the library doesn't give me a built in way to count pairs that meet some predicate. Of course I can build it out of adjacent_find fairly easily,3 but it's not baked in.

My problem at work? Well, that was test code, and the predicate was a lot clearer if you knew where you were in the container you were, so in the end I wrote for (size_t i=1; i<container.size(); ++i) { const auto thing1 = container[i-1]; const auto thing2 = container[i]; // ... } The library routines are great, but knowing when to not use them is great, too.


1 You can also acheive same two operations by using mismatch and transform, though I feel the "adjacent" named routines make the intent clearer. I takes a moment to suss out the meaning of something like const auto [i2, i1] = std::mismatch(++container.begin(), container.end(), container.begin(), my_comparator); aferall, while adjacent_find is pretty plain (though you'll have to parse the behavior of the supplied comparator in either case).

2 I know. I know. That was my mistake.

3 If I had more spare time I'd poke ino some of he standard library implementations to see if people build count_if from find.