Add more implicit conversions from containers to rs_std::SliceRef
This CL tries to achieve it by delegating to `absl::Span`'s constructors.
PiperOrigin-RevId: 663041577
Change-Id: I9db734cf639194bdacb7f0dd4833df5351a03ba0
diff --git a/support/rs_std/BUILD b/support/rs_std/BUILD
index 5bc0537..9f3b650 100644
--- a/support/rs_std/BUILD
+++ b/support/rs_std/BUILD
@@ -48,6 +48,7 @@
# and/or exact absl/base/options.h).
deps = [
"//support/internal:bindings_support",
+ "@abseil-cpp//absl/base",
"@abseil-cpp//absl/base:core_headers",
"@abseil-cpp//absl/types:span",
],
diff --git a/support/rs_std/slice_ref.h b/support/rs_std/slice_ref.h
index 47cfe1b..ca0414e 100644
--- a/support/rs_std/slice_ref.h
+++ b/support/rs_std/slice_ref.h
@@ -5,11 +5,14 @@
#ifndef THIRD_PARTY_CRUBIT_SUPPORT_RS_STD_SLICEREF_H_
#define THIRD_PARTY_CRUBIT_SUPPORT_RS_STD_SLICEREF_H_
+#include <concepts>
#include <cstddef>
#include <cstdint>
#include <span> // NOLINT(build/c++20); <internal link>
+#include <type_traits>
#include "absl/base/attributes.h"
+#include "absl/base/casts.h"
#include "absl/types/span.h"
#include "support/internal/attribute_macros.h"
@@ -40,6 +43,24 @@
: reinterpret_cast<uintptr_t>(span.data())),
size_(span.size()) {}
+ // Re-use implicit conversions to `absl::Span`. Prevent a delegation circle
+ // by excluding `absl::Span<T>` as the converted type.
+ template <typename Container>
+ requires(std::convertible_to<Container &&, absl::Span<T>> &&
+ !std::is_same_v<Container, absl::Span<T>>)
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ SliceRef(Container&& container) noexcept
+ : SliceRef(absl::implicit_cast<absl::Span<T>>(
+ std::forward<Container>(container))) {}
+
+ // Also mirror explicit conversions from `absl::Span`.
+ template <typename Container>
+ requires(std::constructible_from<absl::Span<T>, Container &&> &&
+ !std::convertible_to<Container &&, absl::Span<T>> &&
+ !std::is_same_v<Container, absl::Span<T>>)
+ explicit SliceRef(Container&& container) noexcept
+ : SliceRef(absl::Span<T>(std::forward<Container>(container))) {}
+
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr operator std::span<T>() const {
return std::span<T>(size_ > 0 ? static_cast<T*>(ptr_) : nullptr, size_);
diff --git a/support/rs_std/slice_ref_test.cc b/support/rs_std/slice_ref_test.cc
index dbdcd5e..68ad1ca 100644
--- a/support/rs_std/slice_ref_test.cc
+++ b/support/rs_std/slice_ref_test.cc
@@ -19,6 +19,7 @@
#include "absl/types/span.h"
namespace {
+using ::testing::ElementsAre;
using ::testing::IsNull;
using ::testing::Not;
@@ -75,19 +76,19 @@
static constexpr std::array<uint8_t, 5> kArr = {1, 2, 3, 4, 5};
static constexpr std::array<uint8_t, 5> kArrCopy = {1, 2, 3, 4, 5};
- const rs_std::SliceRef<const uint8_t> s1(kArr);
+ const rs_std::SliceRef<const uint8_t> s1 = kArr;
const rs_std::SliceRef<const uint8_t> s1_copy = s1;
- const rs_std::SliceRef<const uint8_t> s2(kArrCopy);
+ const rs_std::SliceRef<const uint8_t> s2 = kArrCopy;
EXPECT_EQ(s1.to_span(), s1.to_span());
EXPECT_EQ(s1.to_span(), s1_copy.to_span());
EXPECT_EQ(s1.to_span(), s2.to_span());
- const rs_std::SliceRef<const uint8_t> s1_prefix(
- absl::MakeSpan(kArr.data(), kArr.size() - 1));
- const rs_std::SliceRef<const uint8_t> s1_suffix(
- absl::MakeSpan(kArr.data() + 1, kArr.size() - 1));
- const rs_std::SliceRef<const uint8_t> s1_infix(
- absl::MakeSpan(kArr.data() + 1, kArr.size() - 2));
+ const rs_std::SliceRef<const uint8_t> s1_prefix =
+ absl::MakeSpan(kArr.data(), kArr.size() - 1);
+ const rs_std::SliceRef<const uint8_t> s1_suffix =
+ absl::MakeSpan(kArr.data() + 1, kArr.size() - 1);
+ const rs_std::SliceRef<const uint8_t> s1_infix =
+ absl::MakeSpan(kArr.data() + 1, kArr.size() - 2);
EXPECT_NE(s1.to_span(), s1_prefix.to_span());
EXPECT_NE(s1.to_span(), s1_suffix.to_span());
@@ -96,7 +97,7 @@
TEST(SliceTest, FromAndTo) {
static constexpr std::array<uint8_t, 5> kArr = {1, 2, 3, 4, 5};
- const rs_std::SliceRef<const uint8_t> s(kArr);
+ const rs_std::SliceRef<const uint8_t> s = kArr;
EXPECT_EQ(absl::Span<const uint8_t>(kArr), s.to_span());
}
@@ -111,7 +112,7 @@
// `SliceRef` is correct.
TEST(SliceTest, Layout) {
static constexpr std::array<uint8_t, 5> kArr = {2, 3, 5};
- const rs_std::SliceRef<const uint8_t> s(kArr);
+ const rs_std::SliceRef<const uint8_t> s = kArr;
const auto fields = std::bit_cast<SliceRefFields>(s);
EXPECT_EQ(fields.ptr, kArr.data());
EXPECT_EQ(fields.size, kArr.size());
@@ -119,7 +120,7 @@
TEST(SliceTest, Empty) {
static constexpr uint8_t kEmpty[] = {};
- const rs_std::SliceRef<const uint8_t> empty(absl::MakeSpan(kEmpty, 0));
+ const rs_std::SliceRef<const uint8_t> empty = absl::MakeSpan(kEmpty, 0);
static constexpr rs_std::SliceRef<const uint8_t> default_constructed;
EXPECT_EQ(empty.to_span(), default_constructed.to_span());
@@ -133,8 +134,41 @@
EXPECT_EQ(empty.size(), 0);
}
+TEST(ImplicitConversionTest, MutableFromVector) {
+ std::vector<int> vec = {1, 2, 3};
+ // Mirroring `absl::Span`, there is no implicit conversion from mutable
+ // containers.
+ static_assert(!std::convertible_to<std::vector<int>&, rs_std::SliceRef<int>>);
+ // Explicit conversion works.
+ const rs_std::SliceRef<int> from_vec(vec);
+ EXPECT_THAT(from_vec.to_span(), ElementsAre(1, 2, 3));
+}
+
+TEST(ImplicitConversionTest, FromConstVector) {
+ const std::vector<int> vec = {1, 2, 3};
+ const rs_std::SliceRef<const int> from_vec = vec;
+ EXPECT_THAT(from_vec.to_span(), ElementsAre(1, 2, 3));
+}
+
+TEST(ImplicitConversionTest, FromArray) {
+ std::array<int, 2> arr = {1, 2};
+ // Mirroring `absl::Span`, there is no implicit conversion from mutable
+ // containers.
+ static_assert(
+ !std::convertible_to<std::array<int, 2>&, rs_std::SliceRef<int>>);
+ // Explicit conversion works.
+ const rs_std::SliceRef<int> from_arr(arr);
+ EXPECT_THAT(from_arr.to_span(), ElementsAre(1, 2));
+}
+
+TEST(ImplicitConversionTest, FromConstArray) {
+ static constexpr std::array<int, 2> arr = {1, 2};
+ const rs_std::SliceRef<const int> from_arr = arr;
+ EXPECT_THAT(from_arr.to_span(), ElementsAre(1, 2));
+}
+
void Fuzzer(std::vector<uint8_t> data) {
- const rs_std::SliceRef<const uint8_t> s(data);
+ const rs_std::SliceRef<const uint8_t> s = data;
EXPECT_EQ(absl::Span<const uint8_t>(data), s.to_span());
const std::vector<uint8_t> data_copy(s.data(), s.data() + s.size());
EXPECT_EQ(data, data_copy);