1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-25 20:22:30 +01:00

fixed_typemap.hpp: reduce indirection

Backported some changes from auto_typemap.hpp
Implemented methods init(), reset(), clear()
Disabled recreation support.
This commit is contained in:
Nekotekina 2021-02-03 21:14:31 +03:00
parent 8a9320c4ef
commit d788b12a8e
20 changed files with 229 additions and 181 deletions

View File

@ -33,7 +33,6 @@ target_include_directories(rpcs3_emu
# Utilities
target_sources(rpcs3_emu PRIVATE
../util/atomic.cpp
../util/fixed_typemap.cpp
../util/logs.cpp
../util/yaml.cpp
../util/cereal.cpp

View File

@ -429,7 +429,7 @@ void cpu_thread::operator()()
}
}
while (!g_fxo->get<cpu_profiler>())
while (!g_fxo->is_init<cpu_profiler>())
{
if (Emu.IsStopped())
{
@ -1151,7 +1151,7 @@ void cpu_thread::stop_all() noexcept
void cpu_thread::flush_profilers() noexcept
{
if (!g_fxo->get<cpu_profiler>())
if (!g_fxo->is_init<cpu_profiler>())
{
profiler.fatal("cpu_thread::flush_profilers() has been called incorrectly.");
return;

View File

@ -107,7 +107,7 @@ struct msg_dlg_thread_info
thread_ctrl::wait_for(10'000);
}
if (auto manager = g_fxo->get<rsx::overlays::display_manager>())
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
if (auto dlg = manager->get<rsx::overlays::message_dialog>())
{
@ -149,7 +149,7 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr<char> msgString,
const MsgDialogType _type{ type };
if (auto manager = g_fxo->get<rsx::overlays::display_manager>())
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
if (manager->get<rsx::overlays::message_dialog>())
{
@ -433,7 +433,7 @@ error_code cellMsgDialogClose(f32 delay)
const u64 wait_until = get_guest_system_time() + static_cast<s64>(std::max<float>(delay, 0.0f) * 1000);
if (auto manager = g_fxo->get<rsx::overlays::display_manager>())
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
if (auto dlg = manager->get<rsx::overlays::message_dialog>())
{
@ -463,7 +463,7 @@ error_code cellMsgDialogAbort()
{
cellSysutil.warning("cellMsgDialogAbort()");
if (auto manager = g_fxo->get<rsx::overlays::display_manager>())
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
if (auto dlg = manager->get<rsx::overlays::message_dialog>())
{
@ -514,7 +514,7 @@ error_code cellMsgDialogProgressBarSetMsg(u32 progressBarIndex, vm::cptr<char> m
return CELL_MSGDIALOG_ERROR_PARAM;
}
if (auto manager = g_fxo->get<rsx::overlays::display_manager>())
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
if (auto dlg = manager->get<rsx::overlays::message_dialog>())
{
@ -546,7 +546,7 @@ error_code cellMsgDialogProgressBarReset(u32 progressBarIndex)
{
cellSysutil.warning("cellMsgDialogProgressBarReset(progressBarIndex=%d)", progressBarIndex);
if (auto manager = g_fxo->get<rsx::overlays::display_manager>())
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
if (auto dlg = manager->get<rsx::overlays::message_dialog>())
{
@ -578,7 +578,7 @@ error_code cellMsgDialogProgressBarInc(u32 progressBarIndex, u32 delta)
{
cellSysutil.warning("cellMsgDialogProgressBarInc(progressBarIndex=%d, delta=%d)", progressBarIndex, delta);
if (auto manager = g_fxo->get<rsx::overlays::display_manager>())
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
if (auto dlg = manager->get<rsx::overlays::message_dialog>())
{

View File

@ -64,7 +64,7 @@ std::shared_ptr<OskDialogBase> _get_osk_dialog(bool create = false)
return nullptr;
}
if (auto manager = g_fxo->get<rsx::overlays::display_manager>())
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
std::shared_ptr<rsx::overlays::osk_dialog> dlg = std::make_shared<rsx::overlays::osk_dialog>();
osk->dlg = manager->add(dlg);

View File

@ -646,6 +646,6 @@ public:
#include "util/fixed_typemap.hpp"
extern stx::manual_fixed_typemap<void> g_fixed_typemap;
extern stx::manual_typemap<void, 0x20'00000, 128> g_fixed_typemap;
constexpr stx::manual_fixed_typemap<void>* g_fxo = &g_fixed_typemap;
constexpr auto* g_fxo = &g_fixed_typemap;

View File

@ -15,12 +15,12 @@ namespace input
pad::SetIntercepted(intercepted);
if (const auto handler = g_fxo->get<KeyboardHandlerBase>())
if (const auto handler = g_fxo->try_get<KeyboardHandlerBase>())
{
handler->SetIntercepted(intercepted);
}
if (const auto handler = g_fxo->get<MouseHandlerBase>())
if (const auto handler = g_fxo->try_get<MouseHandlerBase>())
{
handler->SetIntercepted(intercepted);
}

View File

@ -718,7 +718,7 @@ namespace vm
// Notify rsx that range has become valid
// Note: This must be done *before* memory gets mapped while holding the vm lock, otherwise
// the RSX might try to invalidate memory that got unmapped and remapped
if (const auto rsxthr = g_fxo->get<rsx::thread>())
if (const auto rsxthr = g_fxo->try_get<rsx::thread>())
{
rsxthr->on_notify_memory_mapped(addr, size);
}
@ -906,7 +906,7 @@ namespace vm
// Notify rsx to invalidate range
// Note: This must be done *before* memory gets unmapped while holding the vm lock, otherwise
// the RSX might try to call VirtualProtect on memory that is already unmapped
if (const auto rsxthr = g_fxo->get<rsx::thread>())
if (const auto rsxthr = g_fxo->get<rsx::thread>(); g_fxo->is_init<rsx::thread>())
{
rsxthr->on_notify_memory_unmapped(addr, size);
}

View File

@ -268,7 +268,7 @@ namespace rsx
// Only update the screen at about 60fps since updating it everytime slows down the process
std::this_thread::sleep_for(16ms);
if (!g_fxo->get<display_manager>())
if (!g_fxo->is_init<display_manager>())
{
rsx_log.fatal("display_manager was improperly destroyed");
break;

View File

@ -656,7 +656,7 @@ namespace rsx
void recover_fifo();
static void fifo_wake_delay(u64 div = 1);
u32 get_fifo_cmd() const;
std::string dump_regs() const override;
void cpu_wait(bs_t<cpu_flag> old) override;
@ -974,7 +974,7 @@ namespace rsx
inline thread* get_current_renderer()
{
return g_fxo->get<rsx::thread>();
return g_fxo->try_get<rsx::thread>();
}
template<bool IsFullLock = false>

View File

@ -55,7 +55,8 @@
LOG_CHANNEL(sys_log, "SYS");
stx::manual_fixed_typemap<void> g_fixed_typemap;
// Preallocate 32 MiB
stx::manual_typemap<void, 0x20'00000, 128> g_fixed_typemap;
bool g_use_rtm = false;
u64 g_rtm_tx_limit1 = 0;
@ -138,7 +139,7 @@ void Emulator::Init(bool add_only)
idm::init();
g_fxo->reset();
g_fxo->init<named_thread<progress_dialog_server>>();
g_fxo->need<named_thread<progress_dialog_server>>();
// Reset defaults, cache them
g_cfg.from_default();
@ -569,7 +570,7 @@ bool Emulator::BootRsxCapture(const std::string& path)
g_cfg.video.disable_on_disk_shader_cache.set(true);
vm::init();
g_fxo->init();
g_fxo->init(false);
// PS3 'executable'
m_state = system_state::ready;
@ -1095,7 +1096,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
m_state = system_state::ready;
GetCallbacks().on_ready();
vm::init();
g_fxo->init();
g_fxo->init(false);
Run(false);
m_force_boot = false;
@ -1591,7 +1592,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
{
ConfigurePPUCache();
g_fxo->init();
g_fxo->init(false);
Emu.GetCallbacks().init_gs_render();
Emu.GetCallbacks().init_pad_handler(m_title_id);
Emu.GetCallbacks().init_kb_handler();
@ -1618,7 +1619,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
m_state = system_state::ready;
GetCallbacks().on_ready();
vm::init();
g_fxo->init();
g_fxo->init(false);
ppu_load_prx(ppu_prx, m_path);
}
else if (spu_exec.open(elf_file) == elf_error::ok)
@ -1627,7 +1628,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
m_state = system_state::ready;
GetCallbacks().on_ready();
vm::init();
g_fxo->init();
g_fxo->init(false);
spu_load_exec(spu_exec);
}
else
@ -1706,7 +1707,7 @@ void Emulator::Run(bool start_playtime)
cpu.state.notify_one(cpu_flag::stop);
});
if (auto thr = g_fxo->get<named_thread<rsx::rsx_replay_thread>>())
if (auto thr = g_fxo->try_get<named_thread<rsx::rsx_replay_thread>>())
{
thr->state -= cpu_flag::stop;
thr->state.notify_one(cpu_flag::stop);
@ -1747,7 +1748,7 @@ bool Emulator::Pause()
idm::select<named_thread<ppu_thread>>(on_select);
idm::select<named_thread<spu_thread>>(on_select);
if (auto rsx = g_fxo->get<rsx::thread>())
if (auto rsx = g_fxo->try_get<rsx::thread>())
{
rsx->state += cpu_flag::dbg_global_pause;
}
@ -1820,7 +1821,7 @@ void Emulator::Resume()
idm::select<named_thread<ppu_thread>>(on_select);
idm::select<named_thread<spu_thread>>(on_select);
if (auto rsx = g_fxo->get<rsx::thread>())
if (auto rsx = g_fxo->try_get<rsx::thread>())
{
// TODO: notify?
rsx->state -= cpu_flag::dbg_global_pause;
@ -1876,7 +1877,7 @@ void Emulator::Stop(bool restart)
GetCallbacks().on_stop();
if (auto rsx = g_fxo->get<rsx::thread>())
if (auto rsx = g_fxo->try_get<rsx::thread>())
{
// TODO: notify?
rsx->state += cpu_flag::exit;
@ -1962,7 +1963,7 @@ bool Emulator::Quit(bool force_quit)
const auto on_exit = []()
{
// Deinitialize object manager to prevent any hanging objects at program exit
*g_fxo = {};
g_fxo->clear();
};
return GetCallbacks().try_to_quit(force_quit, on_exit);
}

View File

@ -111,9 +111,6 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<RuntimeTypeInfo>true</RuntimeTypeInfo>
</ClCompile>
<ClCompile Include="util\fixed_typemap.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Utilities\bin_patch.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>

View File

@ -881,9 +881,6 @@
<ClCompile Include="util\cereal.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="util\fixed_typemap.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="util\cpu_stats.cpp">
<Filter>Utilities</Filter>
</ClCompile>

View File

@ -447,7 +447,8 @@ bool cheat_engine::is_addr_safe(const u32 offset)
if (Emu.IsStopped())
return false;
const auto ppum = g_fxo->get<ppu_module>();
const auto ppum = g_fxo->try_get<ppu_module>();
if (!ppum)
{
log_cheat.fatal("Failed to get ppu_module");

View File

@ -264,7 +264,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
"\nKey R: Registers Editor for selected thread."
"\nKey N: Show next instruction the thread will execute after marked instruction, does nothing if target is not predictable."
"\nKey M: Show the Memory Viewer with initial address pointing to the marked instruction."
"\nKey I: Show RSX method detail."
"\nKey I: Show RSX method detail."
"\nKey F10: Perform single-stepping on instructions."
"\nKey F11: Perform step-over on instructions. (skip function calls)"
"\nKey F1: Show this help dialog."
@ -431,7 +431,7 @@ cpu_thread* debugger_frame::get_cpu()
return m_rsx;
}
if (g_fxo->get<rsx::thread>() != m_rsx)
if (!g_fxo->is_init<rsx::thread>())
{
m_rsx = nullptr;
return m_rsx;

View File

@ -971,7 +971,7 @@ void main_window::DecryptSPRXLibraries()
// Always start with no KLIC
std::vector<u128> klics{u128{}};
if (const auto keys = g_fxo->get<loaded_npdrm_keys>())
if (const auto keys = g_fxo->try_get<loaded_npdrm_keys>())
{
// Second klic: get it from a running game
if (const u128 klic = keys->devKlic)

View File

@ -373,8 +373,8 @@ void Buffer::showImage(const QImage& image)
void Buffer::ShowWindowed()
{
const auto render = rsx::get_current_renderer();
if (!render)
//const auto render = rsx::get_current_renderer();
if (!g_fxo->is_init<rsx::thread>())
return;
// TODO: Is there any better way to choose the color buffers

View File

@ -11,7 +11,7 @@
s32 save_data_dialog::ShowSaveDataList(std::vector<SaveDataEntry>& save_entries, s32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet)
{
// TODO: Install native shell as an Emu callback
if (auto manager = g_fxo->get<rsx::overlays::display_manager>())
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
auto result = manager->create<rsx::overlays::save_dialog>()->show(save_entries, focused, op, listSet);
if (result != rsx::overlays::user_interface::selection_code::error)

View File

@ -7,7 +7,7 @@
s32 trophy_notification_helper::ShowTrophyNotification(const SceNpTrophyDetails& trophy, const std::vector<uchar>& trophy_icon_buffer)
{
if (auto manager = g_fxo->get<rsx::overlays::display_manager>())
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
// Allow adding more than one trophy notification. The notification class manages scheduling
auto popup = std::make_shared<rsx::overlays::trophy_notification>();

View File

@ -1,15 +0,0 @@
#include "fixed_typemap.hpp"
#include <algorithm>
namespace stx::detail
{
void destroy_info::sort_by_reverse_creation_order(destroy_info* begin, destroy_info* end)
{
std::sort(begin, end, [](const destroy_info& a, const destroy_info& b)
{
// Destroy order is the inverse of creation order
return a.created > b.created;
});
}
}

View File

@ -1,54 +1,49 @@
#pragma once
#include <memory>
// Backported from auto_typemap.hpp as a more simple alternative
#include "util/types.hpp"
#include "util/typeindices.hpp"
#include <utility>
#include <type_traits>
#include <util/typeindices.hpp>
namespace stx
{
namespace detail
{
// Destroy list element
struct destroy_info
{
void** object_pointer;
unsigned long long created;
void(*destroy)(void*& ptr) noexcept;
static void sort_by_reverse_creation_order(destroy_info* begin, destroy_info* end);
};
}
// Typemap with exactly one object of each used type, created on init() and destroyed on clear()
template <typename Tag>
class manual_fixed_typemap
// Simplified typemap with exactly one object of each used type, non-moveable. Initialized on init(). Destroyed on clear().
template <typename Tag /*Tag should be unique*/, u32 Size = 0, u32 Align = (Size ? 64 : __STDCPP_DEFAULT_NEW_ALIGNMENT__)>
class alignas(Align) manual_typemap
{
// Save default constructor and destructor
struct typeinfo
{
void(*create)(void*& ptr) noexcept;
void(*destroy)(void*& ptr) noexcept;
bool(*create)(uchar* ptr, manual_typemap&) noexcept;
void(*destroy)(void* ptr) noexcept;
template <typename T>
static void call_ctor(void*& ptr) noexcept
static bool call_ctor(uchar* ptr, manual_typemap& _this) noexcept
{
// Don't overwrite if already exists
if (!ptr)
// Allow passing reference to "this"
if constexpr (std::is_constructible_v<T, manual_typemap&>)
{
// Call default constructor only if available
if constexpr (std::is_default_constructible_v<T>)
{
ptr = new T();
}
new (ptr) T(_this);
return true;
}
// Call default constructor only if available
if constexpr (std::is_default_constructible_v<T>)
{
new (ptr) T();
return true;
}
return false;
}
template <typename T>
static void call_dtor(void*& ptr) noexcept
static void call_dtor(void* ptr) noexcept
{
delete static_cast<T*>(ptr);
ptr = nullptr;
std::launder(static_cast<T*>(ptr))->~T();
}
template <typename T>
@ -61,138 +56,186 @@ namespace stx
}
};
// Raw pointers to existing objects (may be nullptr)
std::unique_ptr<void*[]> m_list;
// Objects
union
{
uchar* m_list = nullptr;
mutable uchar m_data[Size ? Size : 1];
};
// Creation order for each object (used to reverse destruction order)
std::unique_ptr<unsigned long long[]> m_order;
void** m_order = nullptr;
// Used to generate creation order (increased on every construction)
unsigned long long m_init_count = 0;
// Helper for destroying in reverse order
const typeinfo** m_info = nullptr;
// Indicates whether object is created at given index
bool* m_init = nullptr;
public:
constexpr manual_fixed_typemap() noexcept = default;
manual_typemap() noexcept = default;
manual_fixed_typemap(const manual_fixed_typemap&) = delete;
manual_typemap(const manual_typemap&) = delete;
manual_fixed_typemap(manual_fixed_typemap&& r) noexcept
: m_list(std::move(r.m_list))
, m_order(std::move(r.m_order))
, m_init_count(r.m_init_count)
manual_typemap& operator=(const manual_typemap&) = delete;
~manual_typemap()
{
r.m_init_count = 0;
ensure(!m_init);
}
manual_fixed_typemap& operator=(const manual_fixed_typemap&) = delete;
manual_fixed_typemap& operator=(manual_fixed_typemap&& r) noexcept
void reset()
{
manual_fixed_typemap x(std::move(r));
std::swap(m_list, x.m_list);
std::swap(m_order, x.m_order);
std::swap(m_init_count, x.m_init_count);
return *this;
}
~manual_fixed_typemap()
{
if (!m_list && !m_order)
if (m_init)
{
return;
clear();
}
reset();
}
m_order = new void*[stx::typelist<typeinfo>().count()];
m_info = new const typeinfo*[stx::typelist<typeinfo>().count()];
m_init = new bool[stx::typelist<typeinfo>().count()]{};
// Destroy all objects and keep them in uninitialized state, must be called first
void reset() noexcept
{
const auto total_count = stx::typelist<typeinfo>().count();
if (!m_list)
if constexpr (Size == 0)
{
m_list = std::make_unique<void*[]>(total_count);
m_order = std::make_unique<unsigned long long[]>(total_count);
return;
}
using detail::destroy_info;
auto all_data = std::make_unique<destroy_info[]>(stx::typelist<typeinfo>().count());
// Actual number of created objects
unsigned _max = 0;
// Create destroy list
for (auto& type : stx::typelist<typeinfo>())
{
if (m_order[type.index()] == 0)
if (stx::typelist<typeinfo>().align() > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
m_list = static_cast<uchar*>(::operator new(usz{stx::typelist<typeinfo>().size()}, std::align_val_t{stx::typelist<typeinfo>().align()}));
}
else
{
m_list = new uchar[stx::typelist<typeinfo>().size()];
}
}
else
{
ensure(Size >= stx::typelist<typeinfo>().size());
ensure(Align >= stx::typelist<typeinfo>().align());
m_data[0] = 0;
}
}
void init(bool reset = true)
{
if (reset)
{
this->reset();
}
for (const auto& type : stx::typelist<typeinfo>())
{
const u32 id = type.index();
uchar* data = (Size ? +m_data : m_list) + type.pos();
// Allocate initialization order id
if (m_init[id])
{
// Skip object if not created
continue;
}
all_data[_max].object_pointer = &m_list[type.index()];
all_data[_max].created = m_order[type.index()];
all_data[_max].destroy = type.destroy;
// Clear creation order
m_order[type.index()] = 0;
_max++;
if (type.create(data, *this))
{
*m_order++ = data;
*m_info++ = &type;
m_init[id] = true;
}
}
// Sort destroy list according to absolute creation order
destroy_info::sort_by_reverse_creation_order(all_data.get(), all_data.get() + _max);
// Destroy objects in correct order
for (unsigned i = 0; i < _max; i++)
{
all_data[i].destroy(*all_data[i].object_pointer);
}
// Reset creation order since it now may be printed
m_init_count = 0;
}
// Default initialize all objects if possible and not already initialized
void init() noexcept
void clear()
{
for (auto& type : stx::typelist<typeinfo>())
if (!m_init)
{
type.create(m_list[type.index()]);
return;
}
// Allocate initialization order id
if (m_list[type.index()])
// Get actual number of created objects
u32 _max = 0;
for (const auto& type : stx::typelist<typeinfo>())
{
if (m_init[type.index()])
{
m_order[type.index()] = ++m_init_count;
// Skip object if not created
_max++;
}
}
// Destroy objects in reverse order
for (; _max; _max--)
{
(*--m_info)->destroy(*--m_order);
}
// Pointers should be restored to their positions
delete[] m_init;
delete[] m_info;
delete[] m_order;
if constexpr (Size == 0)
{
if (stx::typelist<typeinfo>().align() > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
::operator delete[](m_list, std::align_val_t{stx::typelist<typeinfo>().align()});
}
else
{
delete[] m_list;
}
}
m_init = nullptr;
m_info = nullptr;
m_order = nullptr;
if constexpr (Size == 0)
{
m_list = nullptr;
}
}
// Check if object is not initialized but shall be initialized first (to use in initializing other objects)
template <typename T>
void need() noexcept
{
if (!get<T>())
if (!m_init[stx::typeindex<typeinfo, std::decay_t<T>>()])
{
init<T>();
if constexpr (std::is_constructible_v<T, manual_typemap&>)
{
init<T>(*this);
return;
}
if constexpr (std::is_default_constructible_v<T>)
{
init<T>();
return;
}
}
}
// Explicitly (re)initialize object of type T possibly with dynamic type As and arguments
// Explicitly initialize object of type T possibly with dynamic type As and arguments
template <typename T, typename As = T, typename... Args>
As* init(Args&&... args) noexcept
{
auto& ptr = m_list[stx::typeindex<typeinfo, std::decay_t<T>>()];
if (ptr)
if (std::exchange(m_init[stx::typeindex<typeinfo, std::decay_t<T>, std::decay_t<As>>()], true))
{
delete static_cast<T*>(ptr);
// Already exists, recreation is not supported (may be added later)
return nullptr;
}
As* obj = new std::decay_t<As>(std::forward<Args>(args)...);
m_order[stx::typeindex<typeinfo, std::decay_t<T>>()] = ++m_init_count;
ptr = static_cast<T*>(obj);
As* obj = nullptr;
if constexpr (Size != 0)
{
obj = new (m_data + stx::typeoffset<typeinfo, std::decay_t<T>>()) std::decay_t<As>(std::forward<Args>(args)...);
}
else
{
obj = new (m_list + stx::typeoffset<typeinfo, std::decay_t<T>>()) std::decay_t<As>(std::forward<Args>(args)...);
}
*m_order++ = obj;
*m_info++ = &stx::typedata<typeinfo, std::decay_t<T>, std::decay_t<As>>();
return obj;
}
@ -205,11 +248,36 @@ namespace stx
return init<T>(std::forward<Args>(args)...);
}
// Obtain object pointer (thread safe just against other get calls)
template <typename T>
bool is_init() const noexcept
{
return m_init[stx::typeindex<typeinfo, std::decay_t<T>>()];
}
// Obtain object pointer (may be uninitialized memory)
template <typename T>
T* get() const noexcept
{
return static_cast<T*>(m_list[stx::typeindex<typeinfo, std::decay_t<T>>()]);
if constexpr (Size != 0)
{
return std::launder(reinterpret_cast<T*>(m_data + stx::typeoffset<typeinfo, std::decay_t<T>>()));
}
else
{
return std::launder(reinterpret_cast<T*>(m_list + stx::typeoffset<typeinfo, std::decay_t<T>>()));
}
}
// Obtain object pointer if initialized
template <typename T>
T* try_get() const noexcept
{
if (is_init<T>())
{
[[likely]] return get<T>();
}
[[unlikely]] return nullptr;
}
};
}