diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2024-10-21 12:49:43 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2024-10-21 12:49:43 -0500 |
commit | b2dbcb55e2832c373fecb4033a3ed77e5dbc77aa (patch) | |
tree | 1f294fcf1d85a02db86de3eea2b03393fd89ca5a /include/fud_option.hpp | |
parent | 6a27a2a4032e88fa9154ef0f0741edc584f7a701 (diff) |
Add vector and option.
Diffstat (limited to 'include/fud_option.hpp')
-rw-r--r-- | include/fud_option.hpp | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/include/fud_option.hpp b/include/fud_option.hpp new file mode 100644 index 0000000..ca3954f --- /dev/null +++ b/include/fud_option.hpp @@ -0,0 +1,229 @@ +/* + * libfud + * Copyright 2024 Dominick Allen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FUD_OPTION_HPP +#define FUD_OPTION_HPP + +#include "fud_assert.hpp" + +#include <cstddef> +#include <functional> +#include <new> // IWYU pragma: keep (placement new) +#include <type_traits> + +namespace fud { + +namespace option_detail { + +struct NullOptionType { + enum class NullOptConstructor : char + { + Monostate + }; + + constexpr explicit NullOptionType(NullOptConstructor /*unnamed*/) + { + } +}; + +template <size_t Size> +struct DataArray { + // NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays) + std::byte m_data[Size]; + // NOLINTEND(cppcoreguidelines-avoid-c-arrays) + + constexpr std::byte* data() noexcept + { + return m_data; + } + + [[nodiscard]] constexpr const std::byte* data() const noexcept + { + return m_data; + } + + constexpr bool operator==(const DataArray&) const noexcept = default; + + constexpr void clear() + { + for (size_t idx = 0; idx < Size; ++idx) { + m_data[idx] = std::byte(0); + } + } +}; + +} // namespace option_detail + +inline constexpr option_detail::NullOptionType NullOpt{option_detail::NullOptionType::NullOptConstructor::Monostate}; + +template <typename T> +class Option { + private: + static_assert(!std::is_same_v<T, option_detail::NullOptionType>); + static constexpr bool IsRef = std::is_reference_v<T>; + using ValueType = typename std::remove_reference<T>::type; + static constexpr size_t Size = IsRef ? sizeof(std::reference_wrapper<ValueType>) : sizeof(ValueType); + + public: + constexpr Option() noexcept : m_engaged{false} + { + } + + constexpr Option(option_detail::NullOptionType nullOpt) noexcept : m_engaged{false} + { + static_cast<void>(nullOpt); + } + + constexpr Option(T value) noexcept : m_engaged{true} + { + if constexpr (IsRef) { + new (m_data.data()) std::reference_wrapper<ValueType>(std::ref(value)); + if (!m_engaged) { + std::abort(); + } + } else { + new (m_data.data()) ValueType(value); + if (!m_engaged) { + std::abort(); + } + } + } + + constexpr Option(const Option& rhs) noexcept : m_engaged(rhs.m_engaged), m_data(rhs.m_data) + { + } + + constexpr Option(Option&& rhs) noexcept : m_engaged(rhs.m_engaged), m_data(std::move(rhs.m_data)) + { + rhs.cleanup(); + } + + ~Option() noexcept + { + destroy(); + } + + Option& operator=(const Option& rhs) noexcept + { + if (&rhs == this) { + return *this; + } + destroy(); + m_engaged = rhs.m_engaged; + m_data = rhs.m_data; + return *this; + } + + Option& operator=(Option&& rhs) noexcept + { + destroy(); + m_engaged = rhs.m_engaged; + m_data = std::move(rhs.m_data); + rhs.cleanup(); + return *this; + } + + [[nodiscard]] bool hasValue() const + { + return m_engaged; + } + + operator bool() const { + return hasValue(); + } + + [[nodiscard]] constexpr const ValueType& value() const& + { + fudAssert(m_engaged); + if constexpr (IsRef) { + return *reinterpret_cast<const std::reference_wrapper<ValueType>*>(m_data.data()); + } else { + return *reinterpret_cast<const ValueType*>(m_data.data()); + } + } + + [[nodiscard]] constexpr ValueType& value() & + { + fudAssert(m_engaged); + if constexpr (IsRef) { + return *reinterpret_cast<std::reference_wrapper<ValueType>*>(m_data.data()); + } else { + return *reinterpret_cast<ValueType*>(m_data.data()); + } + } + + [[nodiscard]] constexpr const ValueType&& value() const&& + { + fudAssert(m_engaged); + static_assert(!IsRef); + return *reinterpret_cast<const ValueType*>(m_data.data()); + } + + template <typename F> + constexpr auto map(F&& func) const & -> Option<decltype(std::forward<F>(func)(value()))> + { + using U = decltype(std::forward<F>(func)(value())); + // static_assert(std::is_same_v<decltype(std::forward<F>(func)(value())), Option<U>>()); + if (hasValue()) { + return Option<U>{std::forward<F>(func)(value())}; + } + return Option<U>{NullOpt}; + } + + private: + constexpr void destroy() noexcept + { + if (m_engaged) { + if constexpr (IsRef) { + // reinterpret_cast<std::reference_wrapper<ValueType>*>(m_data.data()); + } else { + reinterpret_cast<ValueType*>(m_data.data())->~ValueType(); + } + cleanup(); + } + } + + constexpr void cleanup() noexcept + { + m_engaged = false; + m_data.clear(); + } + + // alignas(maxAlign) Array<uint8_t, maxSize> priv_m_data; + + alignas(alignof(T)) option_detail::DataArray<Size> m_data{}; + + bool m_engaged; +}; + +namespace test { + +void testOption() +{ + Option<int> intOpt; + static_cast<void>(intOpt); + Option<int&> intRefNull; + static_cast<void>(intRefNull); + int value; + Option<int&> intRefValue{value}; +} + +} // namespace test + +} // namespace fud + +#endif |