mijin2/source/mijin/container/optional.hpp

312 lines
7.2 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(); }
[[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)
{
if constexpr (!std::is_reference_v<TValue>) {
emplace(std::move(other.get()));
}
else {
emplace(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)