Files
fennec/include/fennec/containers/optional.h

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