Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c61140e authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes Ib1090e39,I98aefe7c into main

* changes:
  ftl: non_null improvements
  ftl: contains
parents 67241b38 2e85e1b3
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -24,6 +24,18 @@

namespace android::ftl {

// Determines if a container contains a value. This is a simplified version of the C++23
// std::ranges::contains function.
//
//   const ftl::StaticVector vector = {1, 2, 3};
//   assert(ftl::contains(vector, 1));
//
// TODO: Remove in C++23.
template <typename Container, typename Value>
auto contains(const Container& container, const Value& value) -> bool {
  return std::find(container.begin(), container.end(), value) != container.end();
}

// Adapter for std::find_if that converts the return value from iterator to optional.
//
//   const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv};
+104 −8
Original line number Diff line number Diff line
@@ -68,26 +68,28 @@ class NonNull final {
  constexpr NonNull(const NonNull&) = default;
  constexpr NonNull& operator=(const NonNull&) = default;

  constexpr const Pointer& get() const { return pointer_; }
  constexpr explicit operator const Pointer&() const { return get(); }
  [[nodiscard]] constexpr const Pointer& get() const { return pointer_; }
  [[nodiscard]] constexpr explicit operator const Pointer&() const { return get(); }

  // Move operations. These break the invariant, so care must be taken to avoid subsequent access.

  constexpr NonNull(NonNull&&) = default;
  constexpr NonNull& operator=(NonNull&&) = default;

  constexpr Pointer take() && { return std::move(pointer_); }
  constexpr explicit operator Pointer() && { return take(); }
  [[nodiscard]] constexpr Pointer take() && { return std::move(pointer_); }
  [[nodiscard]] constexpr explicit operator Pointer() && { return take(); }

  // Dereferencing.
  constexpr decltype(auto) operator*() const { return *get(); }
  constexpr decltype(auto) operator->() const { return get(); }
  [[nodiscard]] constexpr decltype(auto) operator*() const { return *get(); }
  [[nodiscard]] constexpr decltype(auto) operator->() const { return get(); }

  [[nodiscard]] constexpr explicit operator bool() const { return !(pointer_ == nullptr); }

  // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions
  // through the passkey idiom, for clear compilation errors.
  template <typename P>
  constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward<P>(pointer)) {
    if (!pointer_) std::abort();
    if (pointer_ == nullptr) std::abort();
  }

 private:
@@ -98,11 +100,13 @@ class NonNull final {
};

template <typename P>
constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> {
[[nodiscard]] constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> {
  using Passkey = typename NonNull<std::decay_t<P>>::Passkey;
  return {Passkey{}, std::forward<P>(pointer)};
}

// NonNull<P> <=> NonNull<Q>

template <typename P, typename Q>
constexpr bool operator==(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
  return lhs.get() == rhs.get();
@@ -113,4 +117,96 @@ constexpr bool operator!=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
  return !operator==(lhs, rhs);
}

template <typename P, typename Q>
constexpr bool operator<(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
  return lhs.get() < rhs.get();
}

template <typename P, typename Q>
constexpr bool operator<=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
  return lhs.get() <= rhs.get();
}

template <typename P, typename Q>
constexpr bool operator>=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
  return lhs.get() >= rhs.get();
}

template <typename P, typename Q>
constexpr bool operator>(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
  return lhs.get() > rhs.get();
}

// NonNull<P> <=> Q

template <typename P, typename Q>
constexpr bool operator==(const NonNull<P>& lhs, const Q& rhs) {
  return lhs.get() == rhs;
}

template <typename P, typename Q>
constexpr bool operator!=(const NonNull<P>& lhs, const Q& rhs) {
  return lhs.get() != rhs;
}

template <typename P, typename Q>
constexpr bool operator<(const NonNull<P>& lhs, const Q& rhs) {
  return lhs.get() < rhs;
}

template <typename P, typename Q>
constexpr bool operator<=(const NonNull<P>& lhs, const Q& rhs) {
  return lhs.get() <= rhs;
}

template <typename P, typename Q>
constexpr bool operator>=(const NonNull<P>& lhs, const Q& rhs) {
  return lhs.get() >= rhs;
}

template <typename P, typename Q>
constexpr bool operator>(const NonNull<P>& lhs, const Q& rhs) {
  return lhs.get() > rhs;
}

// P <=> NonNull<Q>

template <typename P, typename Q>
constexpr bool operator==(const P& lhs, const NonNull<Q>& rhs) {
  return lhs == rhs.get();
}

template <typename P, typename Q>
constexpr bool operator!=(const P& lhs, const NonNull<Q>& rhs) {
  return lhs != rhs.get();
}

template <typename P, typename Q>
constexpr bool operator<(const P& lhs, const NonNull<Q>& rhs) {
  return lhs < rhs.get();
}

template <typename P, typename Q>
constexpr bool operator<=(const P& lhs, const NonNull<Q>& rhs) {
  return lhs <= rhs.get();
}

template <typename P, typename Q>
constexpr bool operator>=(const P& lhs, const NonNull<Q>& rhs) {
  return lhs >= rhs.get();
}

template <typename P, typename Q>
constexpr bool operator>(const P& lhs, const NonNull<Q>& rhs) {
  return lhs > rhs.get();
}

}  // namespace android::ftl

// Specialize std::hash for ftl::NonNull<T>
template <typename P>
struct std::hash<android::ftl::NonNull<P>> {
  std::size_t operator()(const android::ftl::NonNull<P>& ptr) const {
    return std::hash<P>()(ptr.get());
  }
};
+11 −0
Original line number Diff line number Diff line
@@ -23,6 +23,17 @@

namespace android::test {

// Keep in sync with example usage in header file.
TEST(Algorithm, Contains) {
  const ftl::StaticVector vector = {1, 2, 3};
  EXPECT_TRUE(ftl::contains(vector, 1));

  EXPECT_FALSE(ftl::contains(vector, 0));
  EXPECT_TRUE(ftl::contains(vector, 2));
  EXPECT_TRUE(ftl::contains(vector, 3));
  EXPECT_FALSE(ftl::contains(vector, 4));
}

// Keep in sync with example usage in header file.
TEST(Algorithm, FindIf) {
  using namespace std::string_view_literals;
+85 −1
Original line number Diff line number Diff line
@@ -14,12 +14,17 @@
 * limitations under the License.
 */

#include <ftl/algorithm.h>
#include <ftl/non_null.h>
#include <gtest/gtest.h>

#include <memory>
#include <set>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_set>
#include <vector>

namespace android::test {
namespace {
@@ -47,7 +52,7 @@ Pair dupe_if(ftl::NonNull<std::unique_ptr<int>> non_null_ptr, bool condition) {
// Keep in sync with example usage in header file.
TEST(NonNull, Example) {
  const auto string_ptr = ftl::as_non_null(std::make_shared<std::string>("android"));
  std::size_t size;
  std::size_t size{};
  get_length(string_ptr, ftl::as_non_null(&size));
  EXPECT_EQ(size, 7u);

@@ -71,5 +76,84 @@ constexpr StringViewPtr longest(StringViewPtr ptr1, StringViewPtr ptr2) {

static_assert(longest(kApplePtr, kOrangePtr) == kOrangePtr);

static_assert(static_cast<bool>(kApplePtr));

static_assert(std::is_same_v<decltype(ftl::as_non_null(std::declval<const int* const>())),
                             ftl::NonNull<const int*>>);

}  // namespace

TEST(NonNull, SwapRawPtr) {
  int i1 = 123;
  int i2 = 456;
  auto ptr1 = ftl::as_non_null(&i1);
  auto ptr2 = ftl::as_non_null(&i2);

  std::swap(ptr1, ptr2);

  EXPECT_EQ(*ptr1, 456);
  EXPECT_EQ(*ptr2, 123);
}

TEST(NonNull, SwapSmartPtr) {
  auto ptr1 = ftl::as_non_null(std::make_shared<int>(123));
  auto ptr2 = ftl::as_non_null(std::make_shared<int>(456));

  std::swap(ptr1, ptr2);

  EXPECT_EQ(*ptr1, 456);
  EXPECT_EQ(*ptr2, 123);
}

TEST(NonNull, VectorOfRawPtr) {
  int i = 1;
  std::vector<ftl::NonNull<int*>> vpi;
  vpi.push_back(ftl::as_non_null(&i));
  EXPECT_FALSE(ftl::contains(vpi, nullptr));
  EXPECT_TRUE(ftl::contains(vpi, &i));
  EXPECT_TRUE(ftl::contains(vpi, vpi.front()));
}

TEST(NonNull, VectorOfSmartPtr) {
  std::vector<ftl::NonNull<std::shared_ptr<int>>> vpi;
  vpi.push_back(ftl::as_non_null(std::make_shared<int>(2)));
  EXPECT_FALSE(ftl::contains(vpi, nullptr));
  EXPECT_TRUE(ftl::contains(vpi, vpi.front().get()));
  EXPECT_TRUE(ftl::contains(vpi, vpi.front()));
}

TEST(NonNull, SetOfRawPtr) {
  int i = 1;
  std::set<ftl::NonNull<int*>> spi;
  spi.insert(ftl::as_non_null(&i));
  EXPECT_FALSE(ftl::contains(spi, nullptr));
  EXPECT_TRUE(ftl::contains(spi, &i));
  EXPECT_TRUE(ftl::contains(spi, *spi.begin()));
}

TEST(NonNull, SetOfSmartPtr) {
  std::set<ftl::NonNull<std::shared_ptr<int>>> spi;
  spi.insert(ftl::as_non_null(std::make_shared<int>(2)));
  EXPECT_FALSE(ftl::contains(spi, nullptr));
  EXPECT_TRUE(ftl::contains(spi, spi.begin()->get()));
  EXPECT_TRUE(ftl::contains(spi, *spi.begin()));
}

TEST(NonNull, UnorderedSetOfRawPtr) {
  int i = 1;
  std::unordered_set<ftl::NonNull<int*>> spi;
  spi.insert(ftl::as_non_null(&i));
  EXPECT_FALSE(ftl::contains(spi, nullptr));
  EXPECT_TRUE(ftl::contains(spi, &i));
  EXPECT_TRUE(ftl::contains(spi, *spi.begin()));
}

TEST(NonNull, UnorderedSetOfSmartPtr) {
  std::unordered_set<ftl::NonNull<std::shared_ptr<int>>> spi;
  spi.insert(ftl::as_non_null(std::make_shared<int>(2)));
  EXPECT_FALSE(ftl::contains(spi, nullptr));
  EXPECT_TRUE(ftl::contains(spi, spi.begin()->get()));
  EXPECT_TRUE(ftl::contains(spi, *spi.begin()));
}

}  // namespace android::test