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 @@ -58,6 +58,12 @@ StringView.h ) +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 NulloptT {}; + +// Nullopt that can be used and returned +inline constexpr NulloptT 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 { + union { + T StoredValue; + char Placeholder; + }; + bool InUse = false; + +public: + Optional() : Placeholder(0), InUse(false) {} + + Optional(const T &t) : StoredValue(t), InUse(true) {} + + Optional(NulloptT) : Placeholder(0), InUse(false) {} + + Optional(const Optional &t) : StoredValue(t.value()), InUse(true) {} + + ~Optional() { + if (InUse) + StoredValue.~T(); + } + + T value() const { return StoredValue; } + + bool has_value() const { return InUse; } + + void reset() { + if (InUse) + StoredValue.~T(); + InUse = false; + } + + constexpr explicit operator bool() const { return has_value(); } + + constexpr Optional &operator=(const T &item) { + StoredValue = item; + InUse = true; + return *this; + } + + constexpr Optional &operator=(NulloptT) { + reset(); + return *this; + } + + constexpr T &operator*() & { return StoredValue; } + + constexpr const T &operator*() const & { return 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 @@ -80,3 +80,14 @@ DEPENDS libc.src.__support.CPP.blockstore ) + +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,50 @@ +//===-- 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); +} 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"],