303 lines
6.9 KiB
C++
303 lines
6.9 KiB
C++
|
|
#pragma once
|
|
|
|
#if !defined(MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED)
|
|
#define MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED 1
|
|
|
|
#include <cstdint>
|
|
#include <utility>
|
|
#include "../debug/assert.hpp"
|
|
#include "../util/concepts.hpp"
|
|
|
|
namespace mijin
|
|
{
|
|
|
|
//
|
|
// public defines
|
|
//
|
|
|
|
//
|
|
// public constants
|
|
//
|
|
|
|
//
|
|
// public types
|
|
//
|
|
|
|
namespace impl
|
|
{
|
|
template<typename T>
|
|
struct OptionalStorage
|
|
{
|
|
alignas(T) std::uint8_t data[sizeof(T)]; // NOLINT(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
|
|
std::uint8_t used = 0;
|
|
|
|
[[nodiscard]]
|
|
constexpr bool empty() const noexcept { return !used; }
|
|
|
|
template<typename... TArgs>
|
|
constexpr void emplace(TArgs&&... args) noexcept
|
|
{
|
|
MIJIN_ASSERT(!used, "Attempting to emplace in an already set OptionalStorage!");
|
|
used = 1;
|
|
::new (data) T(std::forward<TArgs>(args)...);
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
MIJIN_ASSERT(used, "Attempting to clear an empty OptionalStorage!");
|
|
get().~T();
|
|
used = 0;
|
|
}
|
|
|
|
[[nodiscard]] constexpr T& get() noexcept { return *reinterpret_cast<T*>(data); }
|
|
[[nodiscard]] constexpr const T& get() const noexcept { return *reinterpret_cast<const T*>(data); }
|
|
};
|
|
|
|
template<pointer_type T>
|
|
struct OptionalStorage<T>
|
|
{
|
|
static constexpr T invalidPointer() noexcept { return reinterpret_cast<T>(1); }
|
|
|
|
T ptr = invalidPointer();
|
|
|
|
[[nodiscard]]
|
|
constexpr bool empty() const noexcept { return ptr == invalidPointer(); }
|
|
|
|
void emplace(T value) noexcept
|
|
{
|
|
ptr = value;
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
ptr = invalidPointer();
|
|
}
|
|
|
|
[[nodiscard]] T& get() noexcept { return ptr; }
|
|
[[nodiscard]] const T& get() const noexcept { return ptr; }
|
|
};
|
|
|
|
template<reference_type T>
|
|
struct OptionalStorage<T>
|
|
{
|
|
using pointer_t = std::remove_reference_t<T>*;
|
|
|
|
pointer_t ptr = nullptr;
|
|
|
|
[[nodiscard]]
|
|
constexpr bool empty() const noexcept { return ptr == nullptr; }
|
|
|
|
void emplace(T value) noexcept
|
|
{
|
|
ptr = &value;
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
ptr = nullptr;
|
|
}
|
|
|
|
T get() noexcept { return *ptr; }
|
|
const T get() const noexcept { return *ptr; } // NOLINT(readability-const-return-type) T already is a reference
|
|
};
|
|
}
|
|
struct NullOptional {};
|
|
static constexpr NullOptional NULL_OPTIONAL;
|
|
|
|
template<typename TValue>
|
|
class Optional
|
|
{
|
|
private:
|
|
impl::OptionalStorage<TValue> storage_;
|
|
public:
|
|
constexpr Optional() = default;
|
|
constexpr Optional(NullOptional) noexcept {}
|
|
inline Optional(const Optional& other) noexcept;
|
|
inline Optional(Optional&& other) noexcept;
|
|
inline Optional(TValue value) noexcept;
|
|
inline ~Optional() noexcept;
|
|
public:
|
|
Optional& operator =(const Optional& other) noexcept;
|
|
Optional& operator =(Optional&& other) noexcept;
|
|
Optional& operator =(TValue value) noexcept;
|
|
Optional& operator =(NullOptional) noexcept;
|
|
public:
|
|
[[nodiscard]]
|
|
constexpr bool operator ==(NullOptional) const noexcept { return empty(); }
|
|
template<typename TOther>
|
|
[[nodiscard]]
|
|
constexpr bool operator ==(const Optional<TOther>& other) const noexcept;
|
|
template<typename T>
|
|
[[nodiscard]]
|
|
constexpr bool operator ==(const T& value) const noexcept;
|
|
template<typename T>
|
|
[[nodiscard]]
|
|
constexpr bool operator !=(const T& value) const noexcept { return !(*this == value); }
|
|
[[nodiscard]]
|
|
constexpr explicit operator bool() const noexcept { return !empty(); }
|
|
[[nodiscard]]
|
|
constexpr bool operator !() const noexcept { return empty(); }
|
|
[[nodiscard]]
|
|
constexpr std::remove_reference_t<TValue>& operator*() noexcept { return get(); }
|
|
[[nodiscard]]
|
|
constexpr const std::remove_reference_t<TValue>& operator*() const noexcept { return get(); }
|
|
public:
|
|
template<typename... Types>
|
|
void emplace(Types&&... params) noexcept;
|
|
public:
|
|
[[nodiscard]] inline std::remove_reference_t<TValue>& get() noexcept;
|
|
[[nodiscard]] inline const std::remove_reference_t<TValue>& get() const noexcept;
|
|
[[nodiscard]] constexpr bool empty() const noexcept { return storage_.empty(); }
|
|
inline void reset() noexcept;
|
|
};
|
|
|
|
//
|
|
// public functions
|
|
//
|
|
|
|
template<typename TValue>
|
|
Optional<TValue>::Optional(const Optional& other) noexcept
|
|
{
|
|
if (other) {
|
|
emplace(other.get());
|
|
}
|
|
}
|
|
|
|
template<typename TValue>
|
|
Optional<TValue>::Optional(Optional&& other) noexcept
|
|
{
|
|
if (other)
|
|
{
|
|
emplace(std::move(other.get()));
|
|
other.reset();
|
|
}
|
|
}
|
|
|
|
template<typename TValue>
|
|
Optional<TValue>::Optional(TValue value) noexcept
|
|
{
|
|
if constexpr (std::is_reference_v<TValue>) {
|
|
emplace(value);
|
|
}
|
|
else {
|
|
emplace(std::move(value));
|
|
}
|
|
}
|
|
|
|
template<typename TValue>
|
|
Optional<TValue>::~Optional() noexcept
|
|
{
|
|
reset();
|
|
}
|
|
|
|
template<typename TValue>
|
|
auto Optional<TValue>::operator =(const Optional& other) noexcept -> Optional&
|
|
{
|
|
if (&other == this) {
|
|
return *this;
|
|
}
|
|
reset();
|
|
if (other) {
|
|
emplace(other.get());
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template<typename TValue>
|
|
auto Optional<TValue>::operator =(Optional&& other) noexcept -> Optional&
|
|
{
|
|
if (&other == this) {
|
|
return *this;
|
|
}
|
|
reset();
|
|
if (other)
|
|
{
|
|
emplace(std::move(other.get()));
|
|
other.reset();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template<typename TValue>
|
|
auto Optional<TValue>::operator =(TValue value) noexcept -> Optional&
|
|
{
|
|
if constexpr (std::is_reference_v<TValue>) {
|
|
emplace(value);
|
|
}
|
|
else {
|
|
emplace(std::move(value));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template<typename TValue>
|
|
auto Optional<TValue>::operator =(NullOptional) noexcept -> Optional&
|
|
{
|
|
reset();
|
|
return *this;
|
|
}
|
|
|
|
template<typename TValue>
|
|
template<typename TOther>
|
|
constexpr bool Optional<TValue>::operator ==(const Optional<TOther>& other) const noexcept
|
|
{
|
|
if (empty())
|
|
{
|
|
return other.empty();
|
|
}
|
|
if (!other.empty())
|
|
{
|
|
return get() == other.get();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<typename TValue>
|
|
template<typename T>
|
|
constexpr bool Optional<TValue>::operator ==(const T& value) const noexcept
|
|
{
|
|
if (empty())
|
|
{
|
|
return false;
|
|
}
|
|
return get() == value;
|
|
}
|
|
|
|
template<typename TValue>
|
|
template<typename... Types>
|
|
void Optional<TValue>::emplace(Types&&... params) noexcept
|
|
{
|
|
reset();
|
|
storage_.emplace(std::forward<Types>(params)...);
|
|
MIJIN_ASSERT(!empty(), "Something is wrong.");
|
|
}
|
|
|
|
template<typename TValue>
|
|
inline std::remove_reference_t<TValue>& Optional<TValue>::get() noexcept
|
|
{
|
|
MIJIN_ASSERT(!empty(), "Attempting to fetch value from empty Optional!");
|
|
return storage_.get();
|
|
}
|
|
|
|
template<typename TValue>
|
|
inline const std::remove_reference_t<TValue>& Optional<TValue>::get() const noexcept
|
|
{
|
|
MIJIN_ASSERT(!empty(), "Attempting to fetch value from empty Optional!");
|
|
return storage_.get();
|
|
}
|
|
|
|
template<typename TValue>
|
|
inline void Optional<TValue>::reset() noexcept
|
|
{
|
|
if (empty()) {
|
|
return;
|
|
}
|
|
|
|
storage_.clear();
|
|
}
|
|
|
|
} // namespace mijin
|
|
|
|
#endif // !defined(MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED)
|