The game of Lielow

Via Hacker News for some reason: the game of Lielow, recently invented by Michael Amundsen and Alek Erickson. Lielow is played with checkers on an 8×8 board; the only additional thing you need is something to mark each player’s king (e.g. a couple of coins).

Read More

Playing The Search for Almazar (1981)

Last year I ported Winston Llamas’ The Search for Almazar from BASIC to C for the Z-machine; see “Play The Search for Almazar online” (2021-05-16). At the time I didn’t blog a playthrough of the game; but now that it’s been a year and my memories have faded somewhat, why not do the “All the Adventures” thing again?

This post contains major spoilers for all plot points and puzzles!

I’ll be playing my own port of Almazar, which you can play online here.

Read More

Beware CTAD on reverse_iterator

Consider the following example of an “STL-style algorithm,” taken from a lab exercise in one of my training courses:

template<class It>
bool is_palindrome(It first, It last) {
    while (first != last) {
        --last;
        if (first == last) break;
        if (*first != *last) {
            return false;
        }
        ++first;
    }
    return true;
}

A palindrome is equal to itself when read forwards or backwards, so, I said blithely, we could rewrite it like this:

template<class It>
bool is_palindrome(It first, It last) {
    return std::equal(
        first, last,
        std::reverse_iterator(last),
        std::reverse_iterator(first)
    );
}

But look out — this code has a bug that causes undefined behavior! Do you see it?

Read More

Type-erased InplaceUniquePrintable benefits from noexcept

Previously on this blog: “Type-erased UniquePrintable and PrintableRef (2020-11-24).

Now, here’s a simple type-erased InplaceUniquePrintable (owning, value-semantic, move-only, non-heap-allocating). I don’t claim that this one is bang-out-in-five-minutes-able, but on the other hand, it’s the technique most commonly used in practice. The basic idea is to separate the data of the erased type from its required affordances or behaviors — printing, relocating, and destroying. The data is stored as plain old bytes; the behaviors are stored as function pointers, which point to lambdas initialized in the constructor. I discuss this idea further in “Back to Basics: Type Erasure” (CppCon 2019).

Read More

What I’m reading lately: Scholar’s Stage, Popper, Grug

The other day I enjoyed reading Tanner Greer’s “The World that Twitter Never Made” (July 2022). That essay is a blog-postscript to Greer’s own City Journal essay of the day before, “Our Problems Aren’t Procedural”, which itself was a response to Jonathan Haidt’s Atlantic cover story “After Babel” (May 2022). Haidt had argued that the past decade of “uniquely stupid” American political-and-social culture can basically all be blamed on decays in

social capital (extensive social networks with high levels of trust), strong institutions, and shared stories. Social media has weakened all three.

Read More

Google Timer is dead; long live BigTimer

Yesterday I made a revolting discovery: Google Timer has been quietly discontinued! For years and years, you used to be able to google a phrase like “5 minute timer” and receive — in a privileged position above the Google search results — a little JavaScript widget with a countdown. You could take this tab, pull it out into another little browser window, and stick it in the corner of your screen, to produce an on-screen timer counting down whatever you needed. In the context of a training course, it might be counting down the 30 minutes for a lab exercise, the 8 minutes for a coffee break, the hour for lunch… Having an easily accessible on-screen timer widget, visible to all the attendees, was fantastic.

Read More

Fun with lifetime-extended results of assignment

Previously on this blog: “Fun with conversion-operator lookup” (2021-01-13).

Here’s another simple C++ program that gives different output on three of the four mainstream compilers (Godbolt):

#include <stdio.h>
char messages[][23] = {
  "This is Clang (or EDG)",
  "This is GCC",
  "This is MSVC",
};
int count;
struct X { ~X() { ++count; } };

int main() {
  {
    X& a = (X() = X());
    count = 0;
  }
  puts(messages[count]);
}
Read More

An example where inline constexpr makes a difference

The C++11 and C++14 standard libraries defined many constexpr global variables like this:

constexpr piecewise_construct_t piecewise_construct = piecewise_construct_t();
constexpr allocator_arg_t allocator_arg = allocator_arg_t();
constexpr error_type error_ctype = /*unspecified*/;
constexpr defer_lock_t defer_lock{};
constexpr in_place_t in_place{};
constexpr nullopt_t nullopt(/*unspecified*/{});

In C++17, all of these constexpr variables were respecified as inline constexpr variables. The inline keyword here means the same thing as it does on an inline function: “This entity might be defined in multiple TUs; all those definitions are identical; merge them into one definition at link time.” If you look at the generated code for one of these variables in C++14, you’ll see something like this (Godbolt):

Read More

void versus [[noreturn]]

Yesterday someone on the cpplang Slack evinced confusion over the following code:

int divide1(int a, int b) {
    if (b != 0) {
        return a / b;
    } else {
        throw std::runtime_error("div by zero");
    }
}

void throw_error() {
    throw std::runtime_error("div by zero");
}
int divide2(int a, int b) {
    if (b != 0) {
        return a / b;
    } else {
        throw_error();
    }
}

All compiler vendors agree that divide2 should produce a warning along the lines of “control reaches end of non-void function,” but divide1 is totally fine. Our newbie was confused by this behavior. In the case of divide1, we return a value in the if branch but not in the else branch; the compiler is fine with this, because it knows throw means “this branch doesn’t return anything; it throws.” In divide2 we do the same thing, but just hidden inside the throw_error helper function. Suddenly the compiler is not okay with this.

An expert advised to mark throw_error with the [[noreturn]] attribute, to show the compiler that throw_error really never returns and therefore it can be treated the same as a throw. Our newbie replied:

I don’t understand why that should change things. throw_error’s return type being void already means it never returns anything.

Read More

unordered_multiset’s API affects its big-O

An STL-style container’s performance can be dramatically affected by minor changes to the underlying data structure’s invariants, which in turn can be dramatically constrained by the container’s chosen API. Since the Boost.Unordered performance project has put std::unordered_foo back in the spotlight, I thought this would be a good week to talk about my favorite little-known STL trivia tidbit: std::unordered_multiset’s decision to support .equal_range dramatically affects its performance!

Read More

Three Men Discuss Relativity (1926)

The other week I read (via Hacker News) the 1926 treatise Three Men Discuss Relativity, by the brilliant science educator J.W.N. Sullivan. Wikipedia calls it “one of the earliest non-technical accounts of Albert Einstein’s general theory of relativity,” which Einstein had published about ten years earlier, circa 1915. Although Sullivan’s book seems daunting at 200 pages, it’s really set in quite large type and can be read in one sitting. It’s organized as a Galilean dialogue among three archetypes: the Mathematical Physicist, the Ordinary Intelligent Person, and the Philosopher.

Read More

C++20’s parenthesized aggregate initialization has some downsides

Somehow the topic of P0960 parenthesized aggregate initialization has come up three times in the past week over on the cpplang Slack. The good news is that usually the asker is curious why some reasonable-looking C++20 code fails to compile in C++17 — indicating that C++20’s new rules are arguably more intuitive than C++17’s.

But let’s start at the beginning.

Read More