intial commit
This commit is contained in:
298
source/mijin/container/optional.hpp
Normal file
298
source/mijin/container/optional.hpp
Normal file
@@ -0,0 +1,298 @@
|
||||
|
||||
#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(); }
|
||||
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)
|
||||
Reference in New Issue
Block a user