mirror of
https://github.com/abseil/abseil-cpp.git
synced 2026-06-04 12:07:05 +08:00
Open-source absl::AnySpan
PiperOrigin-RevId: 883174961 Change-Id: Iea8fe182d759350f2b84e78892243867cd9f4be8
This commit is contained in:
committed by
Copybara-Service
parent
efcd129252
commit
522a5a8b51
@@ -450,7 +450,9 @@ set(ABSL_INTERNAL_DLL_FILES
|
||||
"time/time.cc"
|
||||
"time/time.h"
|
||||
"types/any.h"
|
||||
"types/any_span.h"
|
||||
"types/compare.h"
|
||||
"types/internal/any_span.h"
|
||||
"types/internal/span.h"
|
||||
"types/optional.h"
|
||||
"types/optional_ref.h"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
|
||||
load("@rules_cc//cc:cc_library.bzl", "cc_library")
|
||||
load("@rules_cc//cc:cc_test.bzl", "cc_test")
|
||||
load(
|
||||
@@ -119,6 +120,61 @@ cc_test(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "any_span",
|
||||
srcs = ["internal/any_span.h"],
|
||||
hdrs = [
|
||||
"any_span.h",
|
||||
],
|
||||
copts = ABSL_DEFAULT_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
deps = [
|
||||
"//absl/base:config",
|
||||
"//absl/base:core_headers",
|
||||
"//absl/base:nullability",
|
||||
"//absl/base:raw_logging_internal",
|
||||
"//absl/base:throw_delegate",
|
||||
"//absl/meta:type_traits",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "any_span_test",
|
||||
size = "small",
|
||||
srcs = ["any_span_test.cc"],
|
||||
copts = ABSL_TEST_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
deps = [
|
||||
":any_span",
|
||||
":span",
|
||||
"//absl/base:config",
|
||||
"//absl/base:exception_testing",
|
||||
"//absl/base:raw_logging_internal",
|
||||
"//absl/hash:hash_testing",
|
||||
"//absl/meta:type_traits",
|
||||
"//absl/strings",
|
||||
"@googletest//:gtest",
|
||||
"@googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "any_span_benchmark",
|
||||
testonly = True,
|
||||
srcs = ["any_span_benchmark.cc"],
|
||||
tags = ["benchmark"],
|
||||
deps = [
|
||||
":any_span",
|
||||
":span",
|
||||
"//absl/base:config",
|
||||
"//absl/base:raw_logging_internal",
|
||||
"//absl/flags:flag",
|
||||
"//absl/strings",
|
||||
"//absl/strings:string_view",
|
||||
"@google_benchmark//:benchmark_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "optional",
|
||||
hdrs = ["optional.h"],
|
||||
|
||||
@@ -27,6 +27,43 @@ absl_cc_library(
|
||||
PUBLIC
|
||||
)
|
||||
|
||||
absl_cc_library(
|
||||
NAME
|
||||
any_span
|
||||
HDRS
|
||||
"any_span.h"
|
||||
SRCS
|
||||
"internal/any_span.h"
|
||||
COPTS
|
||||
${ABSL_DEFAULT_COPTS}
|
||||
DEPS
|
||||
absl::config
|
||||
absl::core_headers
|
||||
absl::nullability
|
||||
absl::raw_logging_internal
|
||||
absl::throw_delegate
|
||||
absl::type_traits
|
||||
PUBLIC
|
||||
)
|
||||
|
||||
absl_cc_test(
|
||||
NAME
|
||||
any_span_test
|
||||
SRCS
|
||||
"any_span_test.cc"
|
||||
COPTS
|
||||
${ABSL_TEST_COPTS}
|
||||
DEPS
|
||||
absl::config
|
||||
absl::exception_testing
|
||||
absl::hash_testing
|
||||
absl::raw_logging_internal
|
||||
absl::span
|
||||
absl::strings
|
||||
absl::type_traits
|
||||
GTest::gmock_main
|
||||
)
|
||||
|
||||
absl_cc_library(
|
||||
NAME
|
||||
span
|
||||
|
||||
1083
absl/types/any_span.h
Normal file
1083
absl/types/any_span.h
Normal file
File diff suppressed because it is too large
Load Diff
258
absl/types/any_span_benchmark.cc
Normal file
258
absl/types/any_span_benchmark.cc
Normal file
@@ -0,0 +1,258 @@
|
||||
// Copyright 2026 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/any_span.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
ABSL_FLAG(uint64_t, benchmark_container_size, 2000,
|
||||
"The size of the containers to use for benchmarking.");
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace {
|
||||
|
||||
//
|
||||
// Benchmarks and supporting code follows.
|
||||
//
|
||||
|
||||
template <typename T>
|
||||
T MakeElement(int) {
|
||||
ABSL_RAW_LOG(FATAL, "Not implemented.");
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string MakeElement<std::string>(int i) {
|
||||
return absl::StrCat(i, i, i);
|
||||
}
|
||||
|
||||
template <>
|
||||
int MakeElement<int>(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
Container MakeContainer() {
|
||||
const size_t container_size = absl::GetFlag(FLAGS_benchmark_container_size);
|
||||
Container c;
|
||||
for (size_t i = 0; i < container_size; ++i) {
|
||||
c.push_back(
|
||||
MakeElement<typename Container::value_type>(static_cast<int>(i)));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename Container, typename SourceContainer>
|
||||
Container MakePointerContainer(SourceContainer* source) {
|
||||
Container c;
|
||||
for (auto& value : *source) {
|
||||
c.push_back(&value);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
//
|
||||
// Sequential Access Benchmarks
|
||||
//
|
||||
|
||||
// Control run, iterating original container.
|
||||
template <typename Container>
|
||||
void BM_Sequential(benchmark::State& state) {
|
||||
auto a = MakeContainer<Container>();
|
||||
benchmark::DoNotOptimize(a);
|
||||
for (auto _ : state) {
|
||||
for (auto& v : a) {
|
||||
benchmark::DoNotOptimize(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps the container with a Span.
|
||||
template <typename Container>
|
||||
void BM_Sequential_Span(benchmark::State& state) {
|
||||
auto container = MakeContainer<Container>();
|
||||
absl::Span<typename Container::value_type> array_span(container);
|
||||
benchmark::DoNotOptimize(array_span);
|
||||
for (auto _ : state) {
|
||||
for (auto& v : array_span) {
|
||||
benchmark::DoNotOptimize(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same as BM_Sequential_Span, but uses a container of pointers and
|
||||
// dereferences it.
|
||||
template <typename Container>
|
||||
void BM_Sequential_DerefSpan(benchmark::State& state) {
|
||||
using T = typename std::remove_pointer<typename Container::value_type>::type;
|
||||
auto values = MakeContainer<std::vector<T>>();
|
||||
const auto container = MakePointerContainer<Container>(&values);
|
||||
absl::Span<T* const> array_span(container);
|
||||
benchmark::DoNotOptimize(array_span);
|
||||
for (auto _ : state) {
|
||||
for (const auto& v : array_span) {
|
||||
benchmark::DoNotOptimize(*v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps a AnySpan<const T> around a Container. Sequentially reads the
|
||||
// entire span.
|
||||
template <typename Container>
|
||||
void BM_Sequential_AnySpan(benchmark::State& state) {
|
||||
using T = typename Container::value_type;
|
||||
auto container = MakeContainer<Container>();
|
||||
AnySpan<T> span(container);
|
||||
benchmark::DoNotOptimize(span);
|
||||
for (auto _ : state) {
|
||||
for (T& s : span) {
|
||||
benchmark::DoNotOptimize(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same as BM_Sequential_AnySpan, but dereferences the elements.
|
||||
template <typename Container>
|
||||
void BM_Sequential_AnySpanDeref(benchmark::State& state) {
|
||||
using T = typename std::remove_pointer<typename Container::value_type>::type;
|
||||
auto values = MakeContainer<std::vector<T>>();
|
||||
auto container = MakePointerContainer<Container>(&values);
|
||||
AnySpan<T> span(container, any_span_transform::Deref());
|
||||
benchmark::DoNotOptimize(span);
|
||||
for (auto _ : state) {
|
||||
for (auto& s : span) {
|
||||
benchmark::DoNotOptimize(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Random Access Benchmarks
|
||||
//
|
||||
|
||||
// Control run, accessing the original container.
|
||||
template <typename Container>
|
||||
void BM_Random(benchmark::State& state) {
|
||||
auto a = MakeContainer<Container>();
|
||||
auto* b = &a;
|
||||
benchmark::DoNotOptimize(a);
|
||||
benchmark::DoNotOptimize(b);
|
||||
for (auto _ : state) {
|
||||
uint64_t index_seed = 0;
|
||||
for (size_t j = 0; j < a.size(); ++j) {
|
||||
index_seed += 5623;
|
||||
const uint64_t index = index_seed % a.size();
|
||||
benchmark::DoNotOptimize((*b)[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps a Span around a Container. Randomly reads the span.
|
||||
template <typename Container>
|
||||
void BM_Random_Span(benchmark::State& state) {
|
||||
using T = typename Container::value_type;
|
||||
auto v = MakeContainer<Container>();
|
||||
absl::Span<T> array_span(v);
|
||||
benchmark::DoNotOptimize(array_span);
|
||||
for (auto _ : state) {
|
||||
uint64_t index_seed = 0;
|
||||
for (size_t j = 0; j < v.size(); ++j) {
|
||||
index_seed += 5623;
|
||||
const uint64_t index = index_seed % v.size();
|
||||
benchmark::DoNotOptimize(array_span[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps a AnySpan around a Container. Randomly reads the span.
|
||||
template <typename Container>
|
||||
void BM_Random_AnySpan(benchmark::State& state) {
|
||||
using T = typename Container::value_type;
|
||||
auto container = MakeContainer<Container>();
|
||||
AnySpan<T> span(container);
|
||||
benchmark::DoNotOptimize(span);
|
||||
for (auto _ : state) {
|
||||
uint64_t index_seed = 0;
|
||||
for (size_t j = 0; j < container.size(); ++j) {
|
||||
index_seed += 5623;
|
||||
const uint64_t index = index_seed % span.size();
|
||||
benchmark::DoNotOptimize(span[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same as BM_Random_AnySpan, but dereferences elements.
|
||||
template <typename Container>
|
||||
void BM_Random_AnySpanDeref(benchmark::State& state) {
|
||||
using T = typename std::remove_pointer<typename Container::value_type>::type;
|
||||
auto values = MakeContainer<std::vector<T>>();
|
||||
auto container = MakePointerContainer<Container>(&values);
|
||||
AnySpan<T> span(container, any_span_transform::Deref());
|
||||
benchmark::DoNotOptimize(span);
|
||||
for (auto _ : state) {
|
||||
uint64_t index_seed = 0;
|
||||
for (size_t j = 0; j < container.size(); ++j) {
|
||||
index_seed += 5623;
|
||||
const uint64_t index = index_seed % span.size();
|
||||
benchmark::DoNotOptimize(span[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sequential access int.
|
||||
BENCHMARK_TEMPLATE(BM_Sequential, std::vector<int>);
|
||||
BENCHMARK_TEMPLATE(BM_Sequential, std::deque<int>);
|
||||
BENCHMARK_TEMPLATE(BM_Sequential_Span, std::vector<int>);
|
||||
BENCHMARK_TEMPLATE(BM_Sequential_AnySpan, std::vector<int>);
|
||||
BENCHMARK_TEMPLATE(BM_Sequential_AnySpan, std::deque<int>);
|
||||
|
||||
// Sequential access string.
|
||||
BENCHMARK_TEMPLATE(BM_Sequential, std::vector<std::string>);
|
||||
BENCHMARK_TEMPLATE(BM_Sequential, std::deque<std::string>);
|
||||
BENCHMARK_TEMPLATE(BM_Sequential_Span, std::vector<std::string>);
|
||||
BENCHMARK_TEMPLATE(BM_Sequential_AnySpan, std::vector<std::string>);
|
||||
BENCHMARK_TEMPLATE(BM_Sequential_AnySpan, std::deque<std::string>);
|
||||
BENCHMARK_TEMPLATE(BM_Sequential_DerefSpan, std::vector<std::string*>);
|
||||
BENCHMARK_TEMPLATE(BM_Sequential_AnySpanDeref, std::vector<std::string*>);
|
||||
|
||||
// Random access int.
|
||||
BENCHMARK_TEMPLATE(BM_Random, std::vector<int>);
|
||||
BENCHMARK_TEMPLATE(BM_Random, std::deque<int>);
|
||||
BENCHMARK_TEMPLATE(BM_Random_Span, std::vector<int>);
|
||||
BENCHMARK_TEMPLATE(BM_Random_AnySpan, std::vector<int>);
|
||||
BENCHMARK_TEMPLATE(BM_Random_AnySpan, std::deque<int>);
|
||||
|
||||
// Random access string.
|
||||
BENCHMARK_TEMPLATE(BM_Random, std::vector<std::string>);
|
||||
BENCHMARK_TEMPLATE(BM_Random, std::deque<std::string>);
|
||||
BENCHMARK_TEMPLATE(BM_Random_Span, std::vector<std::string>);
|
||||
BENCHMARK_TEMPLATE(BM_Random_AnySpan, std::vector<std::string>);
|
||||
BENCHMARK_TEMPLATE(BM_Random_AnySpan, std::deque<std::string>);
|
||||
BENCHMARK_TEMPLATE(BM_Random_AnySpanDeref, std::vector<std::string*>);
|
||||
|
||||
} // namespace
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
1172
absl/types/any_span_test.cc
Normal file
1172
absl/types/any_span_test.cc
Normal file
File diff suppressed because it is too large
Load Diff
477
absl/types/internal/any_span.h
Normal file
477
absl/types/internal/any_span.h
Normal file
@@ -0,0 +1,477 @@
|
||||
// Copyright 2026 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: internal/any_span.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Helper types and functions used by AnySpan. This file should only be
|
||||
// included by any_span.h.
|
||||
#ifndef ABSL_TYPES_INTERNAL_ANY_SPAN_H_
|
||||
#define ABSL_TYPES_INTERNAL_ANY_SPAN_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/base/optimization.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
|
||||
template <typename T>
|
||||
class AnySpan;
|
||||
|
||||
namespace any_span_transform {
|
||||
struct IdentityT;
|
||||
struct DerefT;
|
||||
} // namespace any_span_transform
|
||||
|
||||
namespace any_span_internal {
|
||||
|
||||
//
|
||||
// IsAnySpan inherits from true_type if T is an instance of AnySpan.
|
||||
//
|
||||
|
||||
template <typename T>
|
||||
struct IsAnySpan : public std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct IsAnySpan<AnySpan<T>> : public std::true_type {};
|
||||
|
||||
// A type suitable for storing any function pointer. The standard allows
|
||||
// function pointers to round-trip through other function pointers, but void* is
|
||||
// implementation defined.
|
||||
using FunPtr = void (*)();
|
||||
|
||||
// Whether a transform will be copied and therefore does not have to outlive
|
||||
// the AnySpan.
|
||||
template <typename Transform>
|
||||
constexpr bool kIsTransformCopied =
|
||||
std::is_function_v<std::remove_pointer_t<Transform>>;
|
||||
|
||||
// Type to pass as extra argument to TransformPtr to ensure that our
|
||||
// assumption about Transform copyability is valid.
|
||||
template <typename Transform>
|
||||
using IsTransformCopied =
|
||||
std::integral_constant<bool, kIsTransformCopied<Transform>>;
|
||||
|
||||
// A pointer to the transform function or functor that should be applied to
|
||||
// elements of the container.
|
||||
class TransformPtr {
|
||||
public:
|
||||
TransformPtr() = default;
|
||||
|
||||
// Construct from ptr to function.
|
||||
template <typename R, typename... Args, typename CopiedTransform>
|
||||
explicit TransformPtr(R (*f)(Args...),
|
||||
CopiedTransform copied_transform [[maybe_unused]])
|
||||
: fun_ptr_(reinterpret_cast<FunPtr>(f)) {
|
||||
static_assert(CopiedTransform::value);
|
||||
}
|
||||
|
||||
// Construct from any other invokable object.
|
||||
template <typename T, typename CopiedTransform>
|
||||
explicit TransformPtr(const T& t,
|
||||
CopiedTransform copied_transform [[maybe_unused]])
|
||||
: ptr_(&t) {
|
||||
static_assert(!CopiedTransform::value);
|
||||
}
|
||||
|
||||
// Casts the pointer to the given type.
|
||||
template <typename Transform>
|
||||
auto get() const {
|
||||
if constexpr (std::is_function_v<Transform>) {
|
||||
return reinterpret_cast<const Transform*>(fun_ptr_);
|
||||
} else if constexpr (std::is_function_v<std::remove_pointer_t<Transform>>) {
|
||||
return reinterpret_cast<const Transform>(fun_ptr_);
|
||||
} else {
|
||||
return static_cast<const Transform*>(ptr_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
const void* ptr_ = nullptr;
|
||||
FunPtr fun_ptr_;
|
||||
};
|
||||
};
|
||||
|
||||
// A void* pointer to the container and transform functor. These are always cast
|
||||
// to the correct type since we create this object and the GetterFunction at the
|
||||
// same time (from one of the Make*Getter functions below).
|
||||
struct TransformedContainer {
|
||||
// The container or array.
|
||||
void* ptr;
|
||||
|
||||
// The transform being applied to the container.
|
||||
TransformPtr transform;
|
||||
};
|
||||
|
||||
// Applies transform (at the correct type) to the argument and returns the
|
||||
// result. Does some validity checking to make sure the result is not a
|
||||
// temporary (proxy containers are not allowed).
|
||||
template <typename T, typename Transform, typename U>
|
||||
T& ApplyTransform(TransformPtr transform, U& u) { // NOLINT(runtime/references)
|
||||
const auto t = transform.get<Transform>();
|
||||
ABSL_RAW_DCHECK(t != nullptr, "pointer cannot be null");
|
||||
|
||||
// If compilation fails here due to dropping a const qualifier, it usually
|
||||
// means you tried to wrap a const container with a non-const AnySpan.
|
||||
//
|
||||
// If compilation fails here due to a reference to a temporary, it usually
|
||||
// means that the container or transform cannot be converted to a reference to
|
||||
// T. AnySpan requires that a valid instance of T exists, conversion to a
|
||||
// value type (such as string -> StringPiece or int -> float) is not
|
||||
// supported.
|
||||
//
|
||||
// If compilation fails here due to taking the address of a temporary object,
|
||||
// it means that the transform is returning a temporary. This is disallowed.
|
||||
// Transforms must return a reference to T, or a reference to an object that
|
||||
// can be safely converted to a reference to T (such as a reference_wrapper or
|
||||
// a class that inherits from T).
|
||||
return *&std::invoke(*t, u); // Failed compilation? See above.
|
||||
}
|
||||
|
||||
// The return type of GetterFunction<T>. GetterFunctions return non-const
|
||||
// references to support mutable -> const conversion of spans without additional
|
||||
// indirection.
|
||||
template <typename T>
|
||||
using GetterFunctionResult = absl::remove_const_t<T>&;
|
||||
|
||||
// A type of function pointer that can return an element of a
|
||||
// TransformedContainer. This is used so that we can type-erase with a simple
|
||||
// function pointer instead of a vtable. Doing things this way has less pointer
|
||||
// indirection.
|
||||
template <typename T>
|
||||
using GetterFunction = GetterFunctionResult<T> (*)(const TransformedContainer&,
|
||||
std::size_t);
|
||||
|
||||
// A GetterFunction that works on arrays.
|
||||
template <typename T, typename Element, typename Transform>
|
||||
GetterFunctionResult<T> GetFromArray(const TransformedContainer& container,
|
||||
std::size_t i) {
|
||||
ABSL_RAW_DCHECK(container.ptr != nullptr, "cannot dereference null pointer");
|
||||
auto* array = static_cast<Element*>(container.ptr);
|
||||
return const_cast<GetterFunctionResult<T>>(
|
||||
ApplyTransform<T, Transform>(container.transform, array[i]));
|
||||
}
|
||||
|
||||
// A GetterFunction that works on containers.
|
||||
template <typename T, typename Container, typename Transform>
|
||||
GetterFunctionResult<T> GetFromContainer(const TransformedContainer& container,
|
||||
std::size_t i) {
|
||||
ABSL_RAW_DCHECK(container.ptr != nullptr, "cannot dereference null pointer");
|
||||
Container& c = *static_cast<Container*>(container.ptr);
|
||||
return const_cast<GetterFunctionResult<T>>(
|
||||
ApplyTransform<T, Transform>(container.transform, c[i]));
|
||||
}
|
||||
|
||||
// A GetterFunction that crashes, indicating an invalid AnySpan has been
|
||||
// accessed..
|
||||
template <typename T>
|
||||
GetterFunctionResult<T> GetFromUninitialized(const TransformedContainer&,
|
||||
std::size_t) {
|
||||
ABSL_RAW_LOG(FATAL, "Uninitialized AnySpan access.");
|
||||
}
|
||||
|
||||
//
|
||||
// ArrayTag and PtrArrayTag are GetterFunctions that are never called. They are
|
||||
// used as a tag so Getter::Get can access flat arrays without a function
|
||||
// pointer.
|
||||
//
|
||||
|
||||
template <typename T>
|
||||
GetterFunctionResult<T> ArrayTag(const TransformedContainer&, std::size_t) {
|
||||
ABSL_RAW_LOG(FATAL, "ArrayTag should never be called.");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
GetterFunctionResult<T> PtrArrayTag(const TransformedContainer&, std::size_t) {
|
||||
ABSL_RAW_LOG(FATAL, "PtrArrayTag should never be called.");
|
||||
}
|
||||
|
||||
//
|
||||
// HasSize<Container> inherets from true_type if Container has a size() member.
|
||||
// false_type otherwise.
|
||||
//
|
||||
|
||||
template <typename, typename = void>
|
||||
struct HasSize : public std::false_type {};
|
||||
|
||||
template <class Container>
|
||||
struct HasSize<Container,
|
||||
absl::void_t<decltype(std::declval<Container&>().size())>>
|
||||
: public std::true_type {};
|
||||
|
||||
//
|
||||
// TypeOfData<Container>::type is the return type of data() if Container has a
|
||||
// data() member. It is NoData otherwise.
|
||||
//
|
||||
|
||||
struct NoData {};
|
||||
|
||||
template <typename, typename = void>
|
||||
struct TypeOfData {
|
||||
using type = NoData;
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
struct TypeOfData<Container,
|
||||
absl::void_t<decltype(std::declval<Container&>().data())>> {
|
||||
using type = decltype(std::declval<Container&>().data());
|
||||
};
|
||||
|
||||
// Element type of container based on operator[].
|
||||
template <class Container>
|
||||
using ElementType = typename std::remove_reference<
|
||||
decltype(std::declval<Container&>()[0])>::type;
|
||||
|
||||
// Element type of container when elements are dereferenced.
|
||||
template <class Container>
|
||||
using DerefElementType = typename std::remove_reference<
|
||||
decltype(*std::declval<ElementType<Container>>())>::type;
|
||||
|
||||
// DataIsValid is true_type if Container has a data() member that returns a
|
||||
// pointer to the type returned by operator[], false_type if there is no data()
|
||||
// member or if the types disagree.
|
||||
template <class Container>
|
||||
using DataIsValid =
|
||||
std::is_same<ElementType<Container>*, typename TypeOfData<Container>::type>;
|
||||
|
||||
// Used to access elements of a container or array.
|
||||
template <typename T>
|
||||
struct Getter {
|
||||
Getter() {}
|
||||
|
||||
// Handle mutable -> const conversion.
|
||||
template <typename LazyT = T, typename = typename std::enable_if<
|
||||
std::is_const<LazyT>::value>::type>
|
||||
explicit Getter(const Getter<absl::remove_const_t<T>>& other) {
|
||||
using MutableT = absl::remove_const_t<T>;
|
||||
if (other.fun == &ArrayTag<MutableT>) {
|
||||
ABSL_RAW_DCHECK(other.offset == 0u, "offset must be zero");
|
||||
fun = &ArrayTag<T>;
|
||||
array = other.array;
|
||||
offset = 0;
|
||||
} else if (other.fun == &PtrArrayTag<MutableT>) {
|
||||
ABSL_RAW_DCHECK(other.offset == 0u, "offset must be zero");
|
||||
fun = &PtrArrayTag<T>;
|
||||
ptr_array = other.ptr_array;
|
||||
offset = 0;
|
||||
} else {
|
||||
fun = other.fun;
|
||||
container = other.container;
|
||||
offset = other.offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the element at the given index.
|
||||
T& Get(std::size_t index) const {
|
||||
ABSL_RAW_DCHECK(fun != nullptr, "pointer cannot be null");
|
||||
if (ABSL_PREDICT_TRUE(fun == &ArrayTag<T>)) {
|
||||
ABSL_RAW_DCHECK(array != nullptr, "pointer cannot be null");
|
||||
return array[index];
|
||||
}
|
||||
if (fun == &PtrArrayTag<T>) {
|
||||
ABSL_RAW_DCHECK(ptr_array != nullptr, "pointer cannot be null");
|
||||
return *ptr_array[index];
|
||||
}
|
||||
return fun(container, index + offset);
|
||||
}
|
||||
|
||||
// Returns a Getter offset into this one by pos.
|
||||
Getter Offset(std::size_t pos) const {
|
||||
// Special case when offset is zero. This safely handles empty spans and
|
||||
// empty containers where data() can be null.
|
||||
if (pos == 0) {
|
||||
return *this;
|
||||
}
|
||||
ABSL_RAW_DCHECK(fun != nullptr, "pointer cannot be null");
|
||||
Getter result;
|
||||
result.fun = fun;
|
||||
if (fun == &ArrayTag<T>) {
|
||||
ABSL_RAW_DCHECK(array != nullptr, "pointer cannot be null");
|
||||
ABSL_RAW_DCHECK(offset == 0u, "offset must be zero");
|
||||
result.array = array + pos;
|
||||
result.offset = 0;
|
||||
} else if (fun == &PtrArrayTag<T>) {
|
||||
ABSL_RAW_DCHECK(ptr_array != nullptr, "pointer cannot be null");
|
||||
ABSL_RAW_DCHECK(offset == 0u, "offset must be zero");
|
||||
result.ptr_array = ptr_array + pos;
|
||||
result.offset = 0;
|
||||
} else {
|
||||
ABSL_RAW_DCHECK(container.ptr != nullptr, "pointer cannot be null");
|
||||
result.container = container;
|
||||
result.offset = offset + pos;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// A pointer to a function (or tag function) that specifies how to get an
|
||||
// element from the array or container.
|
||||
GetterFunction<T> fun = &GetFromUninitialized<T>;
|
||||
|
||||
union {
|
||||
T* array; // Active if fun == ArrayTag<T>.
|
||||
T* const* ptr_array; // Active if fun == PtrArrayTag<T>.
|
||||
TransformedContainer container; // Active for all other fun.
|
||||
};
|
||||
|
||||
// Offset into container. Always 0 for array or ptr_array.
|
||||
std::size_t offset = 0;
|
||||
};
|
||||
|
||||
//
|
||||
// MakeArrayGetter returns a Getter for an array.
|
||||
//
|
||||
// MakeArrayGetterImpl is specialised to use ArrayTag<T> when Element == T and
|
||||
// Transform == IdentityT, and to use PtrArrayTag<T> when Element == T* and
|
||||
// Transform == DerefT.
|
||||
//
|
||||
|
||||
template <typename SpanElement, typename ArrayElement, typename Transform>
|
||||
struct MakeArrayGetterImpl {
|
||||
template <typename U>
|
||||
static Getter<U> Make(ArrayElement* array, const Transform& transform) {
|
||||
Getter<U> result;
|
||||
result.fun = &GetFromArray<U, ArrayElement, Transform>;
|
||||
result.container.ptr = const_cast<void*>(static_cast<const void*>(array));
|
||||
result.container.transform =
|
||||
TransformPtr(transform, IsTransformCopied<Transform>{});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// When the span and the array are the same type.
|
||||
template <typename T>
|
||||
struct MakeArrayGetterImpl<T, T, any_span_transform::IdentityT> {
|
||||
template <typename U>
|
||||
static Getter<U> Make(T* array, const any_span_transform::IdentityT&) {
|
||||
Getter<U> result;
|
||||
result.fun = &ArrayTag<U>;
|
||||
result.array = array;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// If we are dereferencing an array of mutable elements (T*), it is safe to add
|
||||
// constness (const T*).
|
||||
template <typename T>
|
||||
struct MakeArrayGetterImpl<const T, T, any_span_transform::IdentityT>
|
||||
: public MakeArrayGetterImpl<const T, const T,
|
||||
any_span_transform::IdentityT> {};
|
||||
|
||||
// When the array is of pointers to elements of the same type as the span.
|
||||
template <typename T>
|
||||
struct MakeArrayGetterImpl<T, T*, any_span_transform::DerefT> {
|
||||
template <typename U>
|
||||
static Getter<U> Make(T* const* ptr_array,
|
||||
const any_span_transform::DerefT&) {
|
||||
Getter<U> result;
|
||||
result.fun = &PtrArrayTag<U>;
|
||||
result.ptr_array = ptr_array;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// If we are dereferencing an array that is mutable along any extent, it is safe
|
||||
// to add constness.
|
||||
template <typename T>
|
||||
struct MakeArrayGetterImpl<const T, T*, any_span_transform::DerefT>
|
||||
: public MakeArrayGetterImpl<const T, const T*,
|
||||
any_span_transform::DerefT> {};
|
||||
|
||||
template <typename T>
|
||||
struct MakeArrayGetterImpl<const T, T* const, any_span_transform::DerefT>
|
||||
: public MakeArrayGetterImpl<const T, const T*,
|
||||
any_span_transform::DerefT> {};
|
||||
|
||||
template <typename T>
|
||||
struct MakeArrayGetterImpl<const T, const T* const, any_span_transform::DerefT>
|
||||
: public MakeArrayGetterImpl<const T, const T*,
|
||||
any_span_transform::DerefT> {};
|
||||
|
||||
template <typename T, typename Element, typename Transform>
|
||||
Getter<T> MakeArrayGetter(Element* array, const Transform& transform) {
|
||||
return MakeArrayGetterImpl<T, Element, Transform>::template Make<T>(
|
||||
array, transform);
|
||||
}
|
||||
|
||||
//
|
||||
// MakeContainerGetter returns a Getter for a given container. It will pass
|
||||
// container.data() to MakeArrayGetter if possible, which allows element access
|
||||
// to be inline.
|
||||
//
|
||||
// The first argument to MakeArrayGetterImpl is expected to be of type
|
||||
// DataIsValid<container>.
|
||||
//
|
||||
|
||||
template <typename T, typename Container, typename Transform>
|
||||
Getter<T> MakeContainerGetterImpl(
|
||||
std::true_type /* DataIsValid<Container> */,
|
||||
Container& container, // NOLINT(runtime/references)
|
||||
const Transform& transform) {
|
||||
return MakeArrayGetter<T, ElementType<Container>, Transform>(container.data(),
|
||||
transform);
|
||||
}
|
||||
|
||||
template <typename T, typename Container, typename Transform>
|
||||
Getter<T> MakeContainerGetterImpl(
|
||||
std::false_type /* DataIsValid<Container> */,
|
||||
Container& container, // NOLINT(runtime/references)
|
||||
const Transform& transform) {
|
||||
Getter<T> result;
|
||||
result.fun = &GetFromContainer<T, Container, Transform>;
|
||||
result.container.ptr =
|
||||
const_cast<void*>(static_cast<const void*>(&container));
|
||||
result.container.transform =
|
||||
TransformPtr(transform, IsTransformCopied<Transform>{});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename Container, typename Transform>
|
||||
Getter<T> MakeContainerGetter(
|
||||
Container& container, // NOLINT(runtime/references)
|
||||
const Transform& transform) {
|
||||
static_assert(std::is_reference<decltype(container[0])>::value,
|
||||
"AnySpan only works with containers that return a reference "
|
||||
"(no vector<bool>, or containers that return by value).");
|
||||
return MakeContainerGetterImpl<T>(DataIsValid<Container>(), container,
|
||||
transform);
|
||||
}
|
||||
|
||||
// Used for testing. Returns true if the given AnySpan performs inline element
|
||||
// access.
|
||||
template <typename T>
|
||||
bool IsCheap(AnySpan<T> s) {
|
||||
return s.getter_.fun == &ArrayTag<T> || s.getter_.fun == &PtrArrayTag<T>;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool EqualImpl(AnySpan<T> a, AnySpan<T> b) {
|
||||
static_assert(std::is_const<T>::value, "");
|
||||
return std::equal(a.begin(), a.end(), b.begin(), b.end());
|
||||
}
|
||||
|
||||
} // namespace any_span_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TYPES_INTERNAL_ANY_SPAN_H_
|
||||
Reference in New Issue
Block a user