refactor: use function-local statics for platform accessors to dodge SIOF

This commit is contained in:
MatthewBeshay 2026-04-08 20:22:40 +10:00
parent 60410f59bf
commit b032e2a3a0
10 changed files with 110 additions and 22 deletions

View file

@ -1,3 +1,18 @@
#pragma once
#include "IPlatformFilesystem.h" #include "IPlatformFilesystem.h"
extern IPlatformFilesystem& PlatformFilesystem; // Function accessor backed by a function-local static (Meyers singleton).
// Avoids the static-initialization-order fiasco that the previous
// `extern IPlatformFilesystem& PlatformFilesystem;` form had: anything reading PlatformFilesystem
// during another translation unit's static init was UB.
//
// The macro lets the existing call sites keep writing `PlatformFilesystem.foo()`
// without each call site having to add the `()`. The expansion is just
// a call to a function returning a reference, so codegen is unchanged
// once inlined.
namespace platform_internal {
IPlatformFilesystem& PlatformFilesystem_get();
}
#define PlatformFilesystem (::platform_internal::PlatformFilesystem_get())

View file

@ -6,8 +6,12 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
StdFilesystem std_filesystem_instance; namespace platform_internal {
IPlatformFilesystem& PlatformFilesystem = std_filesystem_instance; IPlatformFilesystem& PlatformFilesystem_get() {
static StdFilesystem instance;
return instance;
}
}
IPlatformFilesystem::ReadResult StdFilesystem::readFile(const std::filesystem::path& path, IPlatformFilesystem::ReadResult StdFilesystem::readFile(const std::filesystem::path& path,
void* buffer, std::size_t capacity) { void* buffer, std::size_t capacity) {

View file

@ -1,3 +1,18 @@
#pragma once
#include "IPlatformInput.h" #include "IPlatformInput.h"
extern IPlatformInput& PlatformInput; // Function accessor backed by a function-local static (Meyers singleton).
// Avoids the static-initialization-order fiasco that the previous
// `extern IPlatformInput& PlatformInput;` form had: anything reading PlatformInput
// during another translation unit's static init was UB.
//
// The macro lets the existing call sites keep writing `PlatformInput.foo()`
// without each call site having to add the `()`. The expansion is just
// a call to a function returning a reference, so codegen is unchanged
// once inlined.
namespace platform_internal {
IPlatformInput& PlatformInput_get();
}
#define PlatformInput (::platform_internal::PlatformInput_get())

View file

@ -20,8 +20,12 @@
#include "../InputConstants.h" #include "../InputConstants.h"
#include "../../PlatformTypes.h" #include "../../PlatformTypes.h"
SDL2Input sdl2_input_instance; namespace platform_internal {
IPlatformInput& PlatformInput = sdl2_input_instance; IPlatformInput& PlatformInput_get() {
static SDL2Input instance;
return instance;
}
}
static const int KEY_COUNT = SDL_NUM_SCANCODES; static const int KEY_COUNT = SDL_NUM_SCANCODES;
static const int BTN_COUNT = SDL_CONTROLLER_BUTTON_MAX; static const int BTN_COUNT = SDL_CONTROLLER_BUTTON_MAX;

View file

@ -1,4 +1,18 @@
#include "IPlatformProfile.h" #pragma once
#include "ProfileConstants.h"
extern IPlatformProfile& PlatformProfile; #include "IPlatformProfile.h"
// Function accessor backed by a function-local static (Meyers singleton).
// Avoids the static-initialization-order fiasco that the previous
// `extern IPlatformProfile& PlatformProfile;` form had: anything reading PlatformProfile
// during another translation unit's static init was UB.
//
// The macro lets the existing call sites keep writing `PlatformProfile.foo()`
// without each call site having to add the `()`. The expansion is just
// a call to a function returning a reference, so codegen is unchanged
// once inlined.
namespace platform_internal {
IPlatformProfile& PlatformProfile_get();
}
#define PlatformProfile (::platform_internal::PlatformProfile_get())

View file

@ -7,8 +7,12 @@
#include "../ProfileConstants.h" #include "../ProfileConstants.h"
#include "input/input.h" #include "input/input.h"
StubProfile stub_profile_instance; namespace platform_internal {
IPlatformProfile& PlatformProfile = stub_profile_instance; IPlatformProfile& PlatformProfile_get() {
static StubProfile instance;
return instance;
}
}
namespace { namespace {
constexpr PlayerUID kFakeXuidBase = 0xe000d45248242f2eULL; constexpr PlayerUID kFakeXuidBase = 0xe000d45248242f2eULL;

View file

@ -1,5 +1,6 @@
#include "GLRenderer.h" #include "GLRenderer.h"
#include "renderer/renderer.h"
#include "PlatformTypes.h" #include "PlatformTypes.h"
#include "SDL.h" #include "SDL.h"
#include "SDL_error.h" #include "SDL_error.h"
@ -54,8 +55,12 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
GLRenderer gl_renderer_instance; namespace platform_internal {
IPlatformRenderer& PlatformRenderer = gl_renderer_instance; IPlatformRenderer& PlatformRenderer_get() {
static GLRenderer instance;
return instance;
}
}
// MARK: Shaders // MARK: Shaders

View file

@ -2,9 +2,17 @@
#include "IPlatformRenderer.h" #include "IPlatformRenderer.h"
// Defined in the linked renderer backend (currently // Function accessor backed by a function-local static (Meyers singleton).
// platform/renderer/gl/GLRenderer.cpp). The legacy gl* macro shim used // Avoids the static-initialization-order fiasco that the previous
// by older rendering call sites lives in // `extern IPlatformRenderer& PlatformRenderer;` form had: anything reading PlatformRenderer
// platform/renderer/gl/gl_compat.h, which is brought in via // during another translation unit's static init was UB.
// platform/stubs.h on linux. //
extern IPlatformRenderer& PlatformRenderer; // The macro lets the existing call sites keep writing `PlatformRenderer.foo()`
// without each call site having to add the `()`. The expansion is just
// a call to a function returning a reference, so codegen is unchanged
// once inlined.
namespace platform_internal {
IPlatformRenderer& PlatformRenderer_get();
}
#define PlatformRenderer (::platform_internal::PlatformRenderer_get())

View file

@ -1,3 +1,18 @@
#pragma once
#include "IPlatformStorage.h" #include "IPlatformStorage.h"
extern IPlatformStorage& PlatformStorage; // Function accessor backed by a function-local static (Meyers singleton).
// Avoids the static-initialization-order fiasco that the previous
// `extern IPlatformStorage& PlatformStorage;` form had: anything reading PlatformStorage
// during another translation unit's static init was UB.
//
// The macro lets the existing call sites keep writing `PlatformStorage.foo()`
// without each call site having to add the `()`. The expansion is just
// a call to a function returning a reference, so codegen is unchanged
// once inlined.
namespace platform_internal {
IPlatformStorage& PlatformStorage_get();
}
#define PlatformStorage (::platform_internal::PlatformStorage_get())

View file

@ -6,8 +6,12 @@
#include <string> #include <string>
#include <vector> #include <vector>
StubStorage stub_storage_instance; namespace platform_internal {
IPlatformStorage& PlatformStorage = stub_storage_instance; IPlatformStorage& PlatformStorage_get() {
static StubStorage instance;
return instance;
}
}
static XMARKETPLACE_CONTENTOFFER_INFO s_dummyOffer = {}; static XMARKETPLACE_CONTENTOFFER_INFO s_dummyOffer = {};
static XCONTENT_DATA s_dummyContentData = {}; static XCONTENT_DATA s_dummyContentData = {};