summaryrefslogtreecommitdiff
path: root/include/fud_option.hpp
diff options
context:
space:
mode:
authorDominick Allen <djallen@librehumanitas.org>2024-10-21 12:49:43 -0500
committerDominick Allen <djallen@librehumanitas.org>2024-10-21 12:49:43 -0500
commitb2dbcb55e2832c373fecb4033a3ed77e5dbc77aa (patch)
tree1f294fcf1d85a02db86de3eea2b03393fd89ca5a /include/fud_option.hpp
parent6a27a2a4032e88fa9154ef0f0741edc584f7a701 (diff)
Add vector and option.
Diffstat (limited to 'include/fud_option.hpp')
-rw-r--r--include/fud_option.hpp229
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