Always read the first error message first

In training classes, one of the things I always say (besides “Ask questions!”) is “Always look at the first error message, not the last one.” It’s so tempting to look at the last line of the compiler’s output first: after all, it’s the one that’s right there in front of you when the compiler finishes running. You think, “Well, I need to fix all the errors at some point anyway; might as well start here.” It’s a trap! Suppress that temptation, and scroll up to the first error message first.

Read More

Milner’s lamp

Quoted in “Problems for Solution,” American Mathematical Monthly 28:4 (April 1921):

In the Journal of the Indian Mathematical Society, June 1920 […] A. Narasinga Rao [proposes]: “Determine generally the form of a vessel whose contents are just spilling over in the position of equilibrium, whatever the amount of liquid it contains, (1) when it rests on a horizontal plane; (2) when it is suspended about a horizontal axis.”

This problem is known as “Dr. Milner’s lamp,” after Isaac Milner (1750–1820), Dean of Queen’s College, Cambridge. Milner’s having designed an idiosyncratic lamp is mentioned in the biography written posthumously by his niece, although she doesn’t imply anything mathematically interesting or ideal about the lamp’s shape, merely that it was a well-crafted reading lamp popular among Milner’s colleagues. (As a Fellow of the Royal Society, Milner corresponded with Sir Humphry Davy (1778–1829), inventor of a useful lamp of his own.)

Read More

Hash-colliding string literals on MSVC

In my never-ending quest to make C++ braced initializers {1,2,3} behave more like string literals "abc" (see span should have a converting constructor from initializer_list (2021-10-03), which became P2447; and see my upcoming D2752 “Static storage for braced initializers”), I’ve been looking at how different compilers mangle the symbols for hidden variables such as the guard variables of static locals, the backing variables of structured bindings, and so on. This also led me to look at how the character arrays backing string literals are emitted by different compilers.

Read More

Benchmarking Clang’s -fbuiltin-std-forward

In 2022 we saw a lot of interest (finally!) in the costs of std::move and std::forward. For example, in April Richard Smith landed -fbuiltin-std-forward in Clang; in September Vittorio Romeo lamented “The sad state of debug performance in C++”; and in December the MSVC team landed [[msvc::intrinsic]].

Recall that std::forward<Arg>(arg) should be used only on forwarding references, and that when you do, it’s exactly equivalent to static_cast<Arg&&>(arg), or equivalently decltype(arg)(arg). But historically std::forward has been vastly more expensive to compile, because as far as the compiler is concerned, it’s just a function template that needs to be instantiated, codegenned, inlined, and so on.

Way back in March 2015 — seven and a half years ago! — Louis Dionne did a little compile-time benchmark and found that he could win “an improvement of […] about 13.9%” simply by search-and-replacing all of Boost.Hana’s std::forwards into static_casts. So he did that.

Now, these days, Clang understands std::forward just like it understands strlen. (You can disable that clever behavior with -fno-builtin-strlen, -fno-builtin-std-forward.) As I understand it, this means that Clang will avoid generating debug info for instantiations of std::forward, and also inline it into the AST more eagerly. Basically, Clang can short-circuit some of the compile-time cost of std::forward. But does it short-circuit enough of the cost to win back Louis’s 13.9% improvement? Would that patch from 2015 still pass muster today? Let’s reproduce Louis’s benchmark numbers and find out!

Read More

Replacing std::lock_guard with a factory function

Recently I had to track down a use-after-free bug (which see) in some code that used librdkafka. The gist of it was: We call rd_kafka_new and pass it an rk_conf object containing a bunch of pointers to callbacks. rd_kafka_new either succeeds, in which case it gives us back a valid rd_kafka_t* and we make sure to keep those callback objects alive; or it fails (e.g. due to invalid configuration or resource exhaustion), in which case it gives us back NULL and we throw a C++ exception (and destroy those callback objects). But — here’s the bug, as far as I was able to tell — sometimes rd_kafka_new spawns a broker thread and then returns NULL anyway, without cleaning up the broker thread. And then later on, the broker thread attempts to call one of those callbacks… which we’ve already destroyed, because rd_kafka_new had told us it failed! So that was our use-after-free issue.

This use-after-free bug announced itself via an unusual symptom: an uncaught std::system_error exception.

Read More

Polycube snakes and ouroboroi

In the June 1981 issue of Scientific American (JSTOR; direct link for Wikipedia users) Martin Gardner’s “Mathematical Games” column is titled “The inspired geometrical symmetries of Scott Kim.” Readers of this blog will remember Scott Kim as the creator of many fantastic ambigrams: “Scott Kim’s rotational ambigrams for the Celebration of Mind” (2020-10-18). Most of Gardner’s column indeed focuses on that part of Kim’s oeuvre. But the bit that piqued my interest this week was the following space-filling puzzle:

First we must define a snake. It is a single connected chain of identical unit cubes joined at their faces in such a way that each cube (except for a cube at the end of a chain) is attached face-to-face to exactly two other cubes. The snake may twist in any possible direction, provided no internal cube abuts the face of any cube other than its two immediate neighbors. The snake may, however, twist so that any number of its cubes touch along edges or at corners. A polycube snake may be finite in length, having two end cubes that are each fastened to only one cube, or it may be finite and closed so that it has no ends. A snake may also have just one end and be infinite in length, or it may be infinite and endless in both directions.

We now ask a deceptively simple question. What is the smallest number of snakes needed to fill all space?

Read More

What I’m reading lately: tunnel edition

I’ve been working my way through Best Stories of H.G. Wells (Ballantine, 1960); this morning I read “The Story of the Late Mr. Elvesham” (1897). Highly recommended. It’s got the sort of elaborate Victorian sentence structure and overabundance of novelistic detail that can be delightful when it’s in service of a good plot, and frustratingly plodding when it’s not. In this case, it is. No spoilers.

Read More

Refactoring with =delete

Pro tip: I’ve found =delete quite a useful tool when refactoring overly complicated overload sets in legacy code. Suppose we have an overload set in some legacy code that looks like this:

void foo(const char*, int, bool = false);  // #1
void foo(const char*, bool = false);       // #2
void foo(int, bool = false);               // #3

We suspect that overload #1 is unused. We comment it out, and the codebase still compiles, so we think it’s safe to remove.

Read More