333 lines
7.7 KiB
C++
333 lines
7.7 KiB
C++
// =====================================================================================================================
|
|
// fennec, a free and open source game engine
|
|
// Copyright © 2025 Medusa Slockbower
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
// =====================================================================================================================
|
|
|
|
///
|
|
/// \file optional.h
|
|
/// \brief A header containing the definition for a container with an optionally present variable
|
|
///
|
|
///
|
|
/// \details
|
|
/// \author Medusa Slockbower
|
|
///
|
|
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
|
///
|
|
///
|
|
|
|
#ifndef FENNEC_CONTAINERS_OPTIONAL_H
|
|
#define FENNEC_CONTAINERS_OPTIONAL_H
|
|
|
|
#include <fennec/lang/utility.h>
|
|
#include <fennec/memory/new.h>
|
|
|
|
#include <fennec/lang/assert.h>
|
|
|
|
namespace fennec
|
|
{
|
|
|
|
struct nullopt_t {};
|
|
constexpr nullopt_t nullopt_v = {};
|
|
|
|
#define nullopt nullopt_v
|
|
|
|
///
|
|
/// \brief Structure to hold an optional value.
|
|
/// \tparam T
|
|
template<typename T>
|
|
struct optional {
|
|
|
|
// Definitions =========================================================================================================
|
|
public:
|
|
using reference_t = T&;
|
|
using pointer_t = T*;
|
|
using const_reference_t = T&;
|
|
using const_pointer_t = const T*;
|
|
|
|
|
|
// Constructors ========================================================================================================
|
|
|
|
/// \name Constructors & Destructor
|
|
/// @{
|
|
|
|
///
|
|
/// \brief Default Constructor
|
|
constexpr optional()
|
|
: _root(0)
|
|
, _set(false) {
|
|
}
|
|
|
|
///
|
|
/// \brief Default Constructor
|
|
constexpr optional(nullopt_t)
|
|
: _root(0)
|
|
, _set(false) {
|
|
}
|
|
|
|
///
|
|
/// \brief Type Copy Constructor
|
|
/// \param val the value to initialize the underlying object with
|
|
constexpr optional(const T& val)
|
|
: _val(val)
|
|
, _set(true) {
|
|
}
|
|
|
|
///
|
|
/// \brief Type Move Constructor
|
|
/// \param val the value to initialize the underlying object with
|
|
constexpr optional(T&& val)
|
|
: _val(fennec::forward<T>(val))
|
|
, _set(true) {
|
|
}
|
|
|
|
///
|
|
/// \brief Copy Constructor
|
|
/// \param opt the optional to copy
|
|
constexpr optional(const optional& opt) requires is_copy_assignable_v<T>
|
|
: optional() {
|
|
_set = opt._set;
|
|
if (_set) {
|
|
_val = opt._val;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Move Constructor
|
|
/// \param opt the optional to move
|
|
constexpr optional(optional&& opt) noexcept requires is_move_assignable_v<T>
|
|
: optional() {
|
|
_set = opt._set;
|
|
if (_set) {
|
|
_val = fennec::move(opt._val);
|
|
}
|
|
opt = nullopt;
|
|
}
|
|
|
|
template<typename...ArgsT>
|
|
constexpr optional(ArgsT&&...args)
|
|
: _val(fennec::forward<ArgsT>(args)...)
|
|
, _set(true) {
|
|
}
|
|
|
|
constexpr ~optional() {
|
|
if constexpr(is_fundamental_v<T>) {
|
|
return;
|
|
}
|
|
if (_set) {
|
|
fennec::destruct(&_val);
|
|
}
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Properties ==========================================================================================================
|
|
|
|
/// \name Properties
|
|
/// @{
|
|
|
|
///
|
|
/// \brief Implicit Boolean Check, returns `true` when there is a value contained
|
|
constexpr operator bool() const {
|
|
return _set;
|
|
}
|
|
|
|
///
|
|
/// \returns `true` when there is no held value, `false` otherwise.
|
|
constexpr bool empty() const {
|
|
return not _set;
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Assignment Operators ================================================================================================
|
|
|
|
/// \name Assignment
|
|
/// @{
|
|
|
|
///
|
|
/// \brief Fundamental Type Assignment
|
|
/// \param val The value to set with
|
|
constexpr optional& operator=(nullopt_t) {
|
|
if constexpr(not is_fundamental_v<T>) {
|
|
if (_set) {
|
|
fennec::destruct(&_val);
|
|
}
|
|
}
|
|
_root = '\0';
|
|
_set = false;
|
|
return *this;
|
|
}
|
|
|
|
///
|
|
/// \brief Type Copy Assignment
|
|
/// \param val The value to set with
|
|
constexpr optional& operator=(const T& val) requires is_copy_constructible_v<T> and is_copy_assignable_v<T> {
|
|
if (_set) {
|
|
_val = val;
|
|
} else {
|
|
fennec::construct(&_val, val);
|
|
_set = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
///
|
|
/// \brief Type Move Assignment
|
|
/// \param val The value to set with
|
|
constexpr optional& operator=(T&& val) requires is_move_constructible_v<T> and is_move_assignable_v<T> {
|
|
if (_set) {
|
|
_val = fennec::forward<T>(val);
|
|
} else {
|
|
fennec::construct(&_val, fennec::forward<T>(val));
|
|
_set = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
///
|
|
/// \brief Copy Assignment
|
|
/// \param opt The optional to copy
|
|
constexpr optional& operator=(const optional& opt) requires is_copy_constructible_v<T> and is_copy_assignable_v<T> {
|
|
if (_set != opt._set) {
|
|
_set = opt._set;
|
|
if (_set) { // Construct
|
|
fennec::construct(&_val, opt._val);
|
|
} else { // Destruct
|
|
fennec::destruct(&_val);
|
|
_root = 0;
|
|
}
|
|
} else if (_set) { // Copy Assignment
|
|
_val = opt._val;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
///
|
|
/// \brief Move Assignment
|
|
/// \param opt The optional to move
|
|
constexpr optional& operator=(optional&& opt) noexcept requires is_move_constructible_v<T> and is_move_assignable_v<T> {
|
|
if (_set != opt._set) {
|
|
_set = opt._set;
|
|
if (_set) { // Construct
|
|
fennec::construct(&_val, fennec::move(opt._val));
|
|
} else { // Destruct
|
|
fennec::destruct(&_val);
|
|
_root = 0;
|
|
}
|
|
} else if (_set) { // Copy Assignment
|
|
_val = fennec::move(opt._val);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Access ==============================================================================================================
|
|
|
|
/// \name Access
|
|
/// @{
|
|
|
|
///
|
|
/// \returns A pointer to the value, `nullptr` if there is no value
|
|
constexpr pointer_t operator->() noexcept {
|
|
return _set ? &_val : nullptr;
|
|
}
|
|
|
|
///
|
|
/// \returns A const-qualified pointer to the value, `nullptr` if there is no value
|
|
constexpr const_pointer_t operator->() const noexcept {
|
|
return _set ? &_val : nullptr;
|
|
}
|
|
|
|
///
|
|
/// \brief Dereference Operator
|
|
/// \returns A reference to the value
|
|
constexpr T& operator*() & noexcept {
|
|
assertd(_set, "Attempted to reference the value of an unset optional");
|
|
return _val;
|
|
}
|
|
|
|
///
|
|
/// \brief Const Dereference Operator
|
|
/// \returns A const-qualified reference to the value
|
|
constexpr const T& operator*() const& noexcept {
|
|
assertd(_set, "Attempted to reference the value of an unset optional");
|
|
return _val;
|
|
}
|
|
|
|
///
|
|
/// \brief Dereference Operator
|
|
/// \returns A reference to the value
|
|
constexpr T&& operator*() && noexcept {
|
|
assertd(_set, "Attempted to reference the value of an unset optional");
|
|
return _val;
|
|
}
|
|
|
|
///
|
|
/// \brief Const Dereference Operator
|
|
/// \returns A const-qualified reference to the value
|
|
constexpr const T&& operator*() const&& noexcept {
|
|
assertd(_set, "Attempted to reference the value of an unset optional");
|
|
return _val;
|
|
}
|
|
|
|
/// @}
|
|
|
|
// Modifiers ===========================================================================================================
|
|
|
|
/// \name Modifiers
|
|
/// @{
|
|
|
|
///
|
|
/// \brief Emplace Assignment
|
|
/// \val The optional to move
|
|
template<typename...ArgsT>
|
|
constexpr T& emplace(ArgsT&&...args) {
|
|
if (_set) {
|
|
_val = T(fennec::forward<ArgsT>(args)...);
|
|
} else {
|
|
fennec::construct(&_val, fennec::forward<ArgsT>(args)...);
|
|
_set = true;
|
|
}
|
|
return _val;
|
|
}
|
|
|
|
///
|
|
/// \brief Reset the Optional
|
|
void reset() {
|
|
this->operator=(nullopt);
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
|
|
|
|
private:
|
|
union {
|
|
char _root;
|
|
T _val;
|
|
};
|
|
bool _set;
|
|
};
|
|
|
|
}
|
|
|
|
#endif // FENNEC_CONTAINERS_OPTIONAL_H
|