One of the big ones is that integer constants are 32-bit on LP64 / LLP64 systems unless they are large enough, so e.g.
// Equal to 0x80000000 on ILP32
// Undefined behavior on LP64 / LLP64
size_t max = 1u << (sizeof(size_t) * CHAR_BIT - 1);
// Correct version
size_t max = (size_t)1 << (sizeof(size_t) * CHAR_BIT - 1);
This can also happen with e.g. multiplication #define K 1024
#define M (1024 * K)
#define G (1024 * M)
#define T (1024 * G)
// Undefined behavior, on both 32-bit and 64-bit.
size_t max_object_size = 10 * T;
// Correct version (#1)
size_t max_object_size = 10995116277760;
// Correct version (#2)
static const size_t K = 1024;
static const size_t M = 1024 * M;
... etc ...
Or if you need to align your pointers for some reason, so you do the cast correctly (with uintptr_t) and get void *align_ptr(void *p) {
// Mask is 0xfffffff0 on 32-bit (correct)
// Is also 0xfffffff0 on 64-bit (mistake)
return (void *)(((uintptr_t)p + 0xf) & ~0xf);
// Correct version
return (void *)(((uintptr_t)p + 0xf) & ~(uintptr_t)0xf);
}
Consider that you might do some pointer alignment e.g. to work with SIMD. This stuff isn’t so crazy and if you haven’t been targeting 64-bit, a lot of it can creep into your code base over the years. Legacy projects may not compile even remotely cleanly with warnings enabled, it’s just a fact, so even though warnings / static analysis will catch some of the errors above you’re not safe.This is why so many languages have stricter rules about converting integers to narrower / wider types.
Of course basic examples between 32 vs 64 are easy to find. I guess I should have clarified as the OP sounded like he knew the topic well.
I’ve never dug hard into C++ gaming architectures and the patterns they use.
A large project probably has lots of them. If you are lucky there is a 64 bit version of it. Very often though there isn't so you have to find something equivalent and rewrite all interactions with it.
And there might have been very good reasons for choosing those specific dependencies.
That can take ages upon ages and can be quite demotivating. You might have issue even finding out if you have any problems in your own application after you've spent months on replacing dependencies.
> except in a well-contained module?
This is probably one of the best reasons. Game developers aren't entirely to blame here, middleware developers seem to be sticking to 32bit like balsamic glazing. Even if you could pry their cold dead hands off their archaic architecture, they'd probably make you pay full price for the 64bit upgrade.
As for DAWs? Think about all the the VST plugins out there. Most of which probably aren't maintained and only exist as zip files on the artist's dropbox.
Dropping 32bit is aspirationally sound. It's grossly inconsiderate of the very most obvious aspects of reality.
This is one reason why ecosystems like Java are so valuable! The 64 bit transition was so easy for it because of the common insistence on "pure Java" for portability. Combined with pointer compression 64 bit was hardly noticed.
Forcing it is a net-good perhaps?