diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt --- a/libc/src/__support/CPP/CMakeLists.txt +++ b/libc/src/__support/CPP/CMakeLists.txt @@ -68,6 +68,12 @@ libc.src.__support.integer_to_string ) +add_header_library( + optional + HDRS + optional.h +) + add_header_library( type_traits HDRS diff --git a/libc/src/__support/CPP/optional.h b/libc/src/__support/CPP/optional.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/CPP/optional.h @@ -0,0 +1,81 @@ +//===-- Standalone implementation std::optional -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SUPPORT_CPP_OPTIONAL_H +#define LLVM_LIBC_SRC_SUPPORT_CPP_OPTIONAL_H + +namespace __llvm_libc { +namespace cpp { + +// Trivial nullopt_t struct. +struct nullopt_t {}; + +// nullopt that can be used and returned +inline constexpr nullopt_t nullopt; + +// This is very simple implementation of the std::optional class. There are a +// number of guarantees in the standard that are not made here. +// +// This class will be extended as needed in future. +// +// T currently needs to be a scalar or an object with the following properties: +// - copy constructible +// - copy assignable +// - destructible +template class optional { + + template class OptionalStorage { + public: + union { + E StoredValue; + char Placeholder; + }; + bool InUse = false; + + OptionalStorage() : Placeholder(0), InUse(false) {} + OptionalStorage(const E &t) : StoredValue(t), InUse(true) {} + ~OptionalStorage() { + if (InUse) + StoredValue.~E(); + } + + void reset() { + if (InUse) + StoredValue.~E(); + InUse = false; + } + }; + + OptionalStorage Storage; + +public: + optional() {} + optional(nullopt_t) {} + optional(const T &t) : Storage(t) {} + + T value() const { return Storage.StoredValue; } + + bool has_value() const { return Storage.InUse; } + + void reset() { Storage.reset(); } + + constexpr explicit operator bool() const { return Storage.InUse; } + + constexpr optional &operator=(nullopt_t) { + reset(); + return *this; + } + + constexpr T &operator*() & { return Storage.StoredValue; } + + constexpr const T &operator*() const & { return Storage.StoredValue; } +}; +} // namespace cpp +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SUPPORT_CPP_OPTIONAL_H diff --git a/libc/test/src/__support/CPP/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt --- a/libc/test/src/__support/CPP/CMakeLists.txt +++ b/libc/test/src/__support/CPP/CMakeLists.txt @@ -91,3 +91,13 @@ libc.src.__support.CPP.array_ref libc.src.__support.CPP.stringstream ) + +add_libc_unittest( + optional_test + SUITE + libc_cpp_utils_unittests + SRCS + optional_test.cpp + DEPENDS + libc.src.__support.CPP.optional +) diff --git a/libc/test/src/__support/CPP/optional_test.cpp b/libc/test/src/__support/CPP/optional_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/__support/CPP/optional_test.cpp @@ -0,0 +1,62 @@ +//===-- Unittests for Optional --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/optional.h" +#include "utils/UnitTest/Test.h" + +using __llvm_libc::cpp::nullopt; +using __llvm_libc::cpp::optional; + +// This has clase has two properties for testing: +// 1) No default constructor +// 2) A non-trivial destructor with an observable side-effect +class Contrived { + int *_a; + +public: + Contrived(int *a) : _a(a) {} + ~Contrived() { (*_a)++; } +}; + +TEST(LlvmLibcOptionalTest, Tests) { + optional Trivial1(12); + ASSERT_TRUE(Trivial1.has_value()); + ASSERT_EQ(Trivial1.value(), 12); + ASSERT_EQ(*Trivial1, 12); + Trivial1.reset(); + ASSERT_FALSE(Trivial1.has_value()); + + optional Trivial2(12); + ASSERT_TRUE(Trivial2.has_value()); + Trivial2 = nullopt; + ASSERT_FALSE(Trivial2.has_value()); + + // For this test case, the destructor increments the pointed-to value. + int holding = 1; + optional Complicated(&holding); + // Destructor was run once as part of copying the object. + ASSERT_EQ(holding, 2); + // Destructor was run a second time as part of destruction. + Complicated.reset(); + ASSERT_EQ(holding, 3); + // Destructor was not run a third time as the object is already destroyed. + Complicated.reset(); + ASSERT_EQ(holding, 3); + + // Test that assigning an optional to another works when set + optional Trivial3(12); + optional Trivial4 = Trivial3; + ASSERT_TRUE(Trivial4.has_value()); + ASSERT_EQ(Trivial4.value(), 12); + + // Test that assigning an option to another works when unset + optional Trivial5; + ASSERT_FALSE(Trivial5.has_value()); + optional Trivial6 = Trivial5; + ASSERT_FALSE(Trivial6.has_value()); +} diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -75,6 +75,12 @@ deps = [":libc_root", "__support_cpp_uint"], ) +cc_library( + name = "__support_cpp_optional", + hdrs = ["src/__support/CPP/optional.h"], + deps = [":libc_root"], +) + cc_library( name = "__support_cpp_string_view", hdrs = ["src/__support/CPP/StringView.h"],