From 7da7d62c09fa128e594f3ea97ad5a4a77ec1d67d Mon Sep 17 00:00:00 2001 From: Bronek Kozicki Date: Wed, 4 Mar 2026 15:24:22 +0000 Subject: [PATCH] Start work on pfn::optional, all is TODO now --- include/pfn/optional.hpp | 228 +++++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/pfn/optional.cpp | 25 +++++ 3 files changed, 254 insertions(+) create mode 100644 include/pfn/optional.hpp create mode 100644 tests/pfn/optional.cpp diff --git a/include/pfn/optional.hpp b/include/pfn/optional.hpp new file mode 100644 index 0000000..000781c --- /dev/null +++ b/include/pfn/optional.hpp @@ -0,0 +1,228 @@ +// Copyright (c) 2025 Bronek Kozicki +// +// Distributed under the ISC License. See accompanying file LICENSE.md +// or copy at https://opensource.org/licenses/ISC + +#ifndef INCLUDE_PFN_OPTIONAL +#define INCLUDE_PFN_OPTIONAL + +#include +#include // For anything that is not std::optional or std::make_optional + +#ifdef FWD +#pragma push_macro("FWD") +#define INCLUDE_PFN_OPTIONAL__POP_FWD +#undef FWD +#endif + +// Also defined in fn/detail/fwd_macro.hpp but pfn/* headers are standalone +#define FWD(...) static_cast(__VA_ARGS__) + +#ifdef ASSERT +#pragma push_macro("ASSERT") +#define INCLUDE_PFN_OPTIONAL__POP_ASSERT +#undef ASSERT +#endif + +// LIBFN_ASSERT is a customization point for the user +#ifdef LIBFN_ASSERT +#define ASSERT(...) LIBFN_ASSERT(__VA_ARGS__) +#else +#define ASSERT(...) assert((__VA_ARGS__) == true) +#endif + +namespace pfn { + +// [optional.optional], class template optional +template class optional; // partially freestanding + +// [optional.optional.ref], partial specialization of optional for lvalue reference types +template class optional; // partially freestanding + +template +concept _is_derived_from_optional = requires(T const &t) { // exposition only + [](optional const &) {}(t); +}; + +// [optional.relops], relational operators +template constexpr bool operator==(optional const &, optional const &); +template constexpr bool operator!=(optional const &, optional const &); +template constexpr bool operator<(optional const &, optional const &); +template constexpr bool operator>(optional const &, optional const &); +template constexpr bool operator<=(optional const &, optional const &); +template constexpr bool operator>=(optional const &, optional const &); +template U> +constexpr ::std::compare_three_way_result_t operator<=>(optional const &, optional const &); + +// [optional.nullops], comparison with nullopt +template constexpr bool operator==(optional const &, ::std::nullopt_t) noexcept; +template constexpr ::std::strong_ordering operator<=>(optional const &, ::std::nullopt_t) noexcept; + +// [optional.comp.with.t], comparison with T +template constexpr bool operator==(optional const &, U const &); +template constexpr bool operator==(T const &, optional const &); +template constexpr bool operator!=(optional const &, U const &); +template constexpr bool operator!=(T const &, optional const &); +template constexpr bool operator<(optional const &, U const &); +template constexpr bool operator<(T const &, optional const &); +template constexpr bool operator>(optional const &, U const &); +template constexpr bool operator>(T const &, optional const &); +template constexpr bool operator<=(optional const &, U const &); +template constexpr bool operator<=(T const &, optional const &); +template constexpr bool operator>=(optional const &, U const &); +template constexpr bool operator>=(T const &, optional const &); +template + requires(!_is_derived_from_optional) && ::std::three_way_comparable_with +constexpr ::std::compare_three_way_result_t operator<=>(optional const &, U const &); + +// [optional.specalg], specialized algorithms +template constexpr void swap(optional &, optional &) noexcept(true); // TODO noexcept + +template constexpr optional<::std::decay_t> make_optional(T &&); +template constexpr optional make_optional(Args &&...args); +template +constexpr optional make_optional(::std::initializer_list il, Args &&...args); + +template class optional { +public: + using value_type = T; + + // [optional.ctor], constructors + constexpr optional() noexcept; + constexpr optional(::std::nullopt_t) noexcept; + constexpr optional(optional const &); + constexpr optional(optional &&) noexcept(true); // TODO noexcept + template constexpr explicit optional(::std::in_place_t, Args &&...); + template + constexpr explicit optional(::std::in_place_t, ::std::initializer_list, Args &&...); + template > constexpr explicit(true) optional(U &&); // TODO explicit + template constexpr explicit(true) optional(optional const &); // TODO explicit + template constexpr explicit(true) optional(optional &&); // TODO explicit + + // [optional.dtor], destructor + constexpr ~optional(); + + // [optional.assign], assignment + constexpr optional &operator=(::std::nullopt_t) noexcept; + constexpr optional &operator=(optional const &); + constexpr optional &operator=(optional &&) noexcept(true); // TODO noexcept + template > constexpr optional &operator=(U &&); + template constexpr optional &operator=(optional const &); + template constexpr optional &operator=(optional &&); + template constexpr T &emplace(Args &&...); + template constexpr T &emplace(::std::initializer_list, Args &&...); + + // [optional.swap], swap + constexpr void swap(optional &) noexcept(true); // TODO noexcept + + // [optional.observe], observers + constexpr T const *operator->() const noexcept; + constexpr T *operator->() noexcept; + constexpr T const &operator*() const & noexcept; + constexpr T &operator*() & noexcept; + constexpr T &&operator*() && noexcept; + constexpr T const &&operator*() const && noexcept; + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr T const &value() const &; // freestanding-deleted + constexpr T &value() &; // freestanding-deleted + constexpr T &&value() &&; // freestanding-deleted + constexpr T const &&value() const &&; // freestanding-deleted + template > constexpr T value_or(U &&) const &; + template > constexpr T value_or(U &&) &&; + + // [optional.monadic], monadic operations + template constexpr auto and_then(F &&f) &; + template constexpr auto and_then(F &&f) &&; + template constexpr auto and_then(F &&f) const &; + template constexpr auto and_then(F &&f) const &&; + template constexpr auto transform(F &&f) &; + template constexpr auto transform(F &&f) &&; + template constexpr auto transform(F &&f) const &; + template constexpr auto transform(F &&f) const &&; + template constexpr optional or_else(F &&f) &&; + template constexpr optional or_else(F &&f) const &; + + // [optional.mod], modifiers + constexpr void reset() noexcept; + +private: + union { + ::std::remove_cv_t val; // exposition only + }; +}; + +template optional(T) -> optional; + +template class optional { +public: + using value_type = T; + +public: + // [optional.ref.ctor], constructors + constexpr optional() noexcept = default; + constexpr optional(::std::nullopt_t) noexcept : optional() {} + constexpr optional(optional const &rhs) noexcept = default; + + template constexpr explicit optional(::std::in_place_t, Arg &&arg); + template constexpr explicit(true) optional(U &&u) noexcept(true); // TODO explicit/noexcept + template constexpr explicit(true) optional(optional &rhs) noexcept(true); // TODO explicit/noexcept + template constexpr explicit(true) optional(optional const &rhs) noexcept(true); // TODO explicit/noexcept + template constexpr explicit(true) optional(optional &&rhs) noexcept(true); // TODO explicit/noexcept + template + constexpr explicit(true) optional(optional const &&rhs) noexcept(true); // TODO explicit/noexcept + + constexpr ~optional() = default; + + // [optional.ref.assign], assignment + constexpr optional &operator=(::std::nullopt_t) noexcept; + constexpr optional &operator=(optional const &rhs) noexcept = default; + + template constexpr T &emplace(U &&u) noexcept(true); // TODO noexcept + + // [optional.ref.swap], swap + constexpr void swap(optional &rhs) noexcept; + + // [optional.ref.observe], observers + constexpr T *operator->() const noexcept; + constexpr T &operator*() const noexcept; + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr T &value() const; // freestanding-deleted + template > constexpr ::std::remove_cv_t value_or(U &&u) const; + + // [optional.ref.monadic], monadic operations + template constexpr auto and_then(F &&f) const; + template constexpr optional<::std::invoke_result_t> transform(F &&f) const; + template constexpr optional or_else(F &&f) const; + + // [optional.ref.mod], modifiers + constexpr void reset() noexcept; + +private: + T *val = nullptr; // exposition only + + // [optional.ref.expos], exposition only helper functions + template constexpr void _convert_ref_init_val(U &&u); // exposition only +}; + +} // namespace pfn + +namespace std { +// [optional.hash], hash support +template struct hash<::pfn::optional>; +} // namespace std + +#undef ASSERT + +#ifdef INCLUDE_PFN_OPTIONAL__POP_ASSERT +#pragma pop_macro("ASSERT") +#endif + +#undef FWD + +#ifdef INCLUDE_PFN_OPTIONAL__POP_FWD +#pragma pop_macro("FWD") +#endif + +#endif // INCLUDE_PFN_OPTIONAL diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5534c2b..6c4ce48 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -41,6 +41,7 @@ endforeach() set(TESTS_PFN_SOURCES pfn/expected.cpp + pfn/optional.cpp ) include(TargetGenerator) diff --git a/tests/pfn/optional.cpp b/tests/pfn/optional.cpp new file mode 100644 index 0000000..cc5cc79 --- /dev/null +++ b/tests/pfn/optional.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2025 Bronek Kozicki +// +// Distributed under the ISC License. See accompanying file LICENSE.md +// or copy at https://opensource.org/licenses/ISC + +#ifndef PFN_TEST_VALIDATION +// TODO: Add death tests. Until then, empty definition to avoid false "no coverage" reports +#define LIBFN_ASSERT(...) +#include +using pfn::make_optional; +using pfn::optional; +#else +#include +using std::make_optional; +using std::optional; +#endif + +#include + +#include + +TEST_CASE("dummy") +{ + SUCCEED(); // TODO +}