summaryrefslogtreecommitdiff
path: root/lib/nes/common/containers/box.hh
diff options
context:
space:
mode:
authorHannes Schulze <haschu0103@outlook.de>2025-02-06 16:34:23 +0100
committerHannes Schulze <haschu0103@outlook.de>2025-02-06 16:34:23 +0100
commitd1b61b02b1a6bdaa337ff060eda3d3d5fb67827d (patch)
treede3b1d7f7907e9fc786a1b7491b9bca9b9d296b0 /lib/nes/common/containers/box.hh
parent30b4364c772114303d9045677186a53855ea5343 (diff)
refactor: move containers into separate directory
Diffstat (limited to 'lib/nes/common/containers/box.hh')
-rw-r--r--lib/nes/common/containers/box.hh78
1 files changed, 78 insertions, 0 deletions
diff --git a/lib/nes/common/containers/box.hh b/lib/nes/common/containers/box.hh
new file mode 100644
index 0000000..7bf2848
--- /dev/null
+++ b/lib/nes/common/containers/box.hh
@@ -0,0 +1,78 @@
+#pragma once
+
+#include "nes/common/types.hh"
+#include "nes/common/debug.hh"
+
+namespace nes
+{
+ /// Wrapper around an object, allowing for it to be uninitialized or reassigned even if the object type doesn't
+ /// support it.
+ ///
+ /// Basically a std::unique_ptr but stored in-place (and thus not movable).
+ template<typename T>
+ class box
+ {
+ bool initialized_{ false };
+ alignas(T) u8 buffer_[sizeof(T)];
+
+ public:
+ /// Create an empty box.
+ explicit box() = default;
+
+ ~box()
+ {
+ clear();
+ }
+
+ box(box const&) = delete;
+ box(box&&) = delete;
+ auto operator=(box const&) -> box& = delete;
+ auto operator=(box&&) -> box& = delete;
+
+ auto operator*() -> T&
+ {
+ NES_ASSERT(has_value() && "expected box to be non-empty");
+ return *get();
+ }
+
+ auto operator*() const -> T const&
+ {
+ NES_ASSERT(has_value() && "expected box to be non-empty");
+ return *get();
+ }
+
+ auto operator->() -> T*
+ {
+ NES_ASSERT(has_value() && "expected box to be non-empty");
+ return get();
+ }
+
+ auto operator->() const -> T const*
+ {
+ NES_ASSERT(has_value() && "expected box to be non-empty");
+ return get();
+ }
+
+ operator bool() const { return has_value(); }
+
+ auto has_value() const -> bool { return initialized_; }
+ auto get() -> T* { return has_value() ? std::launder(reinterpret_cast<T*>(buffer_)) : nullptr; }
+ auto get() const -> T const* { return has_value() ? std::launder(reinterpret_cast<T const *>(buffer_)) : nullptr; }
+
+ /// Construct a new value in-place, potentially replacing the old value.
+ template<typename... Args>
+ auto emplace(Args&&... args) -> void
+ {
+ clear();
+ new (buffer_) T(std::forward<Args>(args)...);
+ initialized_ = true;
+ }
+
+ /// Clear the boxed value.
+ auto clear() -> void
+ {
+ if (initialized_) { (*this)->~T(); }
+ initialized_ = false;
+ }
+ };
+} // namespace nes \ No newline at end of file