Archive for September, 2024

Constructing Strings from Pieces Without a Dynamic Buffer

Monday, September 30th, 2024

Say I want to make a string out of a couple different pieces. Say we’ve got some input strings a and b and I want to make some kind of debug description, like maybe a: <a>, b: <b>. Shouldn’t be too hard, right? Slap it together:

std::string result = "a: " + a + ", b: " + b;

Easy, right? How many allocations does this incur, though? It could be more than one. We can do better than that – we know up-front everything going into it, so surely we can allocate the right amount of space up-front. How would that look?

(more…)

Subversion on the Cheap

Monday, September 23rd, 2024

You could read this in the manual, or you could read it here. Up to you! Anyway, I like to store code on my own machines. For Git, it’s super easy to have a copy of a repository on another machine that you push to and pull from: you just use machine:path as the remote, and Git will invoke SSH for you (ssh://machine/path also works). Can you do something similar for Subversion?

Sure can! On the host machine, create the repository with svnadmin create:

svnadmin create myrepo

On the client machine, you’ll need a URL of the form svn+ssh://machine/absolutepath:

svn checkout svn+ssh://myhost/home/myuser/myrepo

And that’s it! SVN will do the same sort of “tunnel over SSH” thing that Git does.

Locking when No One’s Looking

Monday, September 16th, 2024

You’re getting ready to start a thread. But wait! There are some variables you need to get ready before then. Those variables are read by the thread. How do you know the new values of those variables will be visible to the thread?

var = 1;
thread = std::thread([this] {
    use(var);  // Will I see "1"?
});

One conservative approach might be to obtain a lock while modifying those variables. This would make a lot of sense, particularly if those variables are typically guarded by that lock during the lifetime of the thread. At the same time, it does feel a little silly obtaining a lock that you know will never be contended, as the only other thing that would lock that mutex has not started yet.

{
    std::lock_guard lock(mutex);
    var = 1;
}
thread = std::thread([this] {
    std::lock_guard lock(mutex);  // Even without this
    use(var);
});

Modifying those variables under a lock is for-sure safe: releasing the lock after modifying will do a “release” memory barrier, ensuring all writes done will be visible. The thread reading them has not started yet, so there is no possible way anything could be reordered too early to see the release. If the thread also locks the mutex before reading those values, it is even more obviously safe.

But is any of that really needed? Intuitively it shouldn’t be needed, but memory models are notoriously tricky things, so let’s check our assumptions.

(more…)

Survey of Platform-Native WebSocket Implementations

Monday, September 9th, 2024

My game is multi-player, so naturally, it needs some way to communicate over the network. Building on the theme of starting with Web-platform technologies, I plan for most of the real-time communication to happen through WebSocket. But despite using Web-platform technologies, the primary deployment environment for my game is not a Web browser, so I need a native implementation I can plug into my game.

(more…)

MSVC Trickiness: CRTP Dependencies

Monday, September 2nd, 2024

I got complacent developing my game on a Mac and Linux boxes so far, and have neglected to actually compile my game on Windows, even though I from the start did intend Windows as a first-class deployment target. I’ve finally gotten around to it, and to no surprise, it’s not been particularly smooth. But some of the problems have been surprising to me.

One I had not previously encountered is this: One portion of my game uses a CRTP (Curiously Recurring Template Pattern) base, that abstracts away some repetitive bits that several derived types need. It looked something like this:

struct Meta {
    const char *stuff;
};

template <typename Derived>
struct Helper {
    static constexpr Meta kMeta = { Derived::kStuff };
};

struct Foo : Helper<Foo> {
    static constexpr const char *kStuff = "stuff";
};

This works fine on GCC, Clang, and ICC. But MSVC?

error C2039: 'kStuff': is not a member of 'Derived'

No way!

(more…)