diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 842c6b3..f7d6b56 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake
@@ -367,6 +367,7 @@ "strings/internal/string_constant.h" "strings/internal/stringify_sink.cc" "strings/internal/stringify_sink.h" + "strings/internal/stringify_stream.h" "strings/internal/utf8.cc" "strings/internal/utf8.h" "strings/match.cc" @@ -450,10 +451,13 @@ "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" + "types/source_location.cc" "types/source_location.h" "types/span.h" "types/variant.h"
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index c0b8a10..3636f8e 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h
@@ -197,8 +197,8 @@ // Container-based version of the <algorithm> `std::for_each()` function to // apply a function to a container's elements. template <typename C, typename Function> -ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 decay_t<Function> c_for_each(C&& c, - Function&& f) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::decay_t<Function> c_for_each( + C&& c, Function&& f) { return std::for_each(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward<Function>(f)); @@ -1758,10 +1758,10 @@ // accumulation by value. // // Note: Due to a language technicality this function has return type -// absl::decay_t<T>. As a user of this function you can casually read +// std::decay_t<T>. As a user of this function you can casually read // this as "returns T by value" and assume it does the right thing. template <typename Sequence, typename T> -ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 decay_t<T> c_accumulate( +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::decay_t<T> c_accumulate( const Sequence& sequence, T&& init) { return std::accumulate(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), @@ -1771,7 +1771,7 @@ // Overload of c_accumulate() for using a binary operations other than // addition for computing the accumulation. template <typename Sequence, typename T, typename BinaryOp> -ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 decay_t<T> c_accumulate( +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::decay_t<T> c_accumulate( const Sequence& sequence, T&& init, BinaryOp&& binary_op) { return std::accumulate(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), @@ -1785,10 +1785,10 @@ // to compute the cumulative inner product of container element pairs. // // Note: Due to a language technicality this function has return type -// absl::decay_t<T>. As a user of this function you can casually read +// std::decay_t<T>. As a user of this function you can casually read // this as "returns T by value" and assume it does the right thing. template <typename Sequence1, typename Sequence2, typename T> -ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 decay_t<T> c_inner_product( +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::decay_t<T> c_inner_product( const Sequence1& factors1, const Sequence2& factors2, T&& sum) { return std::inner_product(container_algorithm_internal::c_begin(factors1), container_algorithm_internal::c_end(factors1), @@ -1801,7 +1801,7 @@ // the product between the two container's element pair). template <typename Sequence1, typename Sequence2, typename T, typename BinaryOp1, typename BinaryOp2> -ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 decay_t<T> c_inner_product( +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::decay_t<T> c_inner_product( const Sequence1& factors1, const Sequence2& factors2, T&& sum, BinaryOp1&& op1, BinaryOp2&& op2) { return std::inner_product(container_algorithm_internal::c_begin(factors1),
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index c6e4be6..a0e71ed 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel
@@ -1011,6 +1011,7 @@ linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", + "//absl:friends", ], deps = [ ":config", @@ -1040,6 +1041,7 @@ linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", + "//absl:friends", ], deps = [ "//absl/base:config",
diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 91e552f..327b222 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h
@@ -531,6 +531,24 @@ #define ABSL_XRAY_LOG_ARGS(N) #endif +// ABSL_ATTRIBUTE_NULL_AFTER_MOVE +// +// Indicates that a user-defined smart-pointer-like type makes guarantees on the +// state of a moved-from object, leaving it in a null state, where it can be +// used as long as it is not dereferenced. In other words, these are the same +// semantics that smart pointers from the standard library provide. +// +// The clang-tidy check bugprone-use-after-move allows member functions of types +// marked with this attribute to be called on objects that have been moved from; +// without the attribute, this would result in a use-after-move warning. +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate) +#define ABSL_ATTRIBUTE_NULL_AFTER_MOVE \ + [[clang::annotate("clang-tidy", "bugprone-use-after-move", \ + "null_after_move")]] +#else +#define ABSL_ATTRIBUTE_NULL_AFTER_MOVE +#endif + // ABSL_ATTRIBUTE_REINITIALIZES // // Indicates that a member function reinitializes the entire object to a known
diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc index 55a6fe1..fc7e33e 100644 --- a/absl/base/exception_safety_testing_test.cc +++ b/absl/base/exception_safety_testing_test.cc
@@ -945,8 +945,8 @@ } TEST(ThrowingAllocatorTraitsTest, Assignablility) { - EXPECT_TRUE(absl::is_move_assignable<ThrowingAllocator<int>>::value); - EXPECT_TRUE(absl::is_copy_assignable<ThrowingAllocator<int>>::value); + EXPECT_TRUE(std::is_move_assignable<ThrowingAllocator<int>>::value); + EXPECT_TRUE(std::is_copy_assignable<ThrowingAllocator<int>>::value); EXPECT_TRUE(std::is_nothrow_move_assignable<ThrowingAllocator<int>>::value); EXPECT_TRUE(std::is_nothrow_copy_assignable<ThrowingAllocator<int>>::value); }
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h index c106154..29f934f 100644 --- a/absl/base/internal/exception_safety_testing.h +++ b/absl/base/internal/exception_safety_testing.h
@@ -14,6 +14,8 @@ // Utilities for testing exception-safety +// SKIP_ABSL_INLINE_NAMESPACE_CHECK + #ifndef ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_ #define ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_ @@ -38,28 +40,29 @@ #include "absl/strings/substitute.h" #include "absl/utility/utility.h" +// TODO(b/500018833): Update the namespace as appropriate. namespace testing { enum class TypeSpec; enum class AllocSpec; constexpr TypeSpec operator|(TypeSpec a, TypeSpec b) { - using T = absl::underlying_type_t<TypeSpec>; + using T = std::underlying_type_t<TypeSpec>; return static_cast<TypeSpec>(static_cast<T>(a) | static_cast<T>(b)); } constexpr TypeSpec operator&(TypeSpec a, TypeSpec b) { - using T = absl::underlying_type_t<TypeSpec>; + using T = std::underlying_type_t<TypeSpec>; return static_cast<TypeSpec>(static_cast<T>(a) & static_cast<T>(b)); } constexpr AllocSpec operator|(AllocSpec a, AllocSpec b) { - using T = absl::underlying_type_t<AllocSpec>; + using T = std::underlying_type_t<AllocSpec>; return static_cast<AllocSpec>(static_cast<T>(a) | static_cast<T>(b)); } constexpr AllocSpec operator&(AllocSpec a, AllocSpec b) { - using T = absl::underlying_type_t<AllocSpec>; + using T = std::underlying_type_t<AllocSpec>; return static_cast<AllocSpec>(static_cast<T>(a) & static_cast<T>(b)); } @@ -842,7 +845,7 @@ template <size_t LazyContractsCount, typename LazyFactory, typename LazyOperation> -using EnableIfTestable = typename absl::enable_if_t< +using EnableIfTestable = typename std::enable_if_t< LazyContractsCount != 0 && !std::is_same<LazyFactory, UninitializedT>::value && !std::is_same<LazyOperation, UninitializedT>::value>; @@ -994,7 +997,7 @@ * method tester.WithInitialValue(...). */ template <typename NewFactory> - ExceptionSafetyTestBuilder<absl::decay_t<NewFactory>, Operation, Contracts...> + ExceptionSafetyTestBuilder<std::decay_t<NewFactory>, Operation, Contracts...> WithFactory(const NewFactory& new_factory) const { return {new_factory, operation_, contracts_}; } @@ -1005,7 +1008,7 @@ * newly created tester. */ template <typename NewOperation> - ExceptionSafetyTestBuilder<Factory, absl::decay_t<NewOperation>, Contracts...> + ExceptionSafetyTestBuilder<Factory, std::decay_t<NewOperation>, Contracts...> WithOperation(const NewOperation& new_operation) const { return {factory_, new_operation, contracts_}; } @@ -1025,11 +1028,11 @@ */ template <typename... MoreContracts> ExceptionSafetyTestBuilder<Factory, Operation, Contracts..., - absl::decay_t<MoreContracts>...> + std::decay_t<MoreContracts>...> WithContracts(const MoreContracts&... more_contracts) const { return { factory_, operation_, - std::tuple_cat(contracts_, std::tuple<absl::decay_t<MoreContracts>...>( + std::tuple_cat(contracts_, std::tuple<std::decay_t<MoreContracts>...>( more_contracts...))}; }
diff --git a/absl/base/internal/low_level_scheduling.h b/absl/base/internal/low_level_scheduling.h index 9baccc0..bba27ef 100644 --- a/absl/base/internal/low_level_scheduling.h +++ b/absl/base/internal/low_level_scheduling.h
@@ -18,8 +18,11 @@ #ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ #define ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ +#include <atomic> + #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/scheduling_mode.h" +#include "absl/base/internal/thread_identity.h" #include "absl/base/macros.h" // The following two declarations exist so SchedulingGuard may friend them with @@ -64,7 +67,6 @@ SchedulingGuard(const SchedulingGuard&) = delete; SchedulingGuard& operator=(const SchedulingGuard&) = delete; - private: // Disable cooperative rescheduling of the calling thread. It may still // initiate scheduling operations (e.g. wake-ups), however, it may not itself // reschedule. Nestable. The returned result is opaque, clients should not @@ -96,13 +98,6 @@ private: int scheduling_disabled_depth_; }; - - // Access to SchedulingGuard is explicitly permitted. - friend class absl::CondVar; - friend class absl::Mutex; - friend class SchedulingHelper; - friend class SpinLock; - friend int absl::synchronization_internal::MutexDelay(int32_t c, int mode); }; //------------------------------------------------------------------------------ @@ -110,21 +105,88 @@ //------------------------------------------------------------------------------ inline bool SchedulingGuard::ReschedulingIsAllowed() { - return false; + ThreadIdentity* identity = CurrentThreadIdentityIfPresent(); + if (identity != nullptr) { + ThreadIdentity::SchedulerState* state = &identity->scheduler_state; + // For a thread to be eligible for re-scheduling it must have a bound + // schedulable (otherwise it's not cooperative) and not be within a + // SchedulerGuard region. + return state->bound_schedulable.load(std::memory_order_relaxed) != + nullptr && + state->scheduling_disabled_depth.load(std::memory_order_relaxed) == + 0; + } else { + // Cooperative threads always have a ThreadIdentity. + return false; + } } +// We don't use [[nodiscard]] here as some clients (e.g. +// FinishPotentiallyBlockingRegion()) cannot yet properly consume it. inline bool SchedulingGuard::DisableRescheduling() { - return false; + ThreadIdentity* identity; + identity = CurrentThreadIdentityIfPresent(); + if (identity != nullptr) { + // The depth is accessed concurrently from other threads, so it must be + // atomic, but it's only mutated from this thread, so we don't need an + // atomic increment. + int old_val = identity->scheduler_state.scheduling_disabled_depth.load( + std::memory_order_relaxed); + identity->scheduler_state.scheduling_disabled_depth.store( + old_val + 1, std::memory_order_relaxed); + return true; + } else { + return false; + } } -inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) { - return; +inline void SchedulingGuard::EnableRescheduling(bool disable_result) { + if (!disable_result) { + // There was no installed thread identity at the time that scheduling was + // disabled, so we have nothing to do. This is an implementation detail + // that may change in the future, clients may not depend on it. + // EnableRescheduling() must always be called. + return; + } + + ThreadIdentity* identity; + // A thread identity exists, see above + identity = CurrentThreadIdentityIfPresent(); + // The depth is accessed concurrently from other threads, so it must be + // atomic, but it's only mutated from this thread, so we don't need an atomic + // decrement. + int old_val = identity->scheduler_state.scheduling_disabled_depth.load( + std::memory_order_relaxed); + identity->scheduler_state.scheduling_disabled_depth.store( + old_val - 1, std::memory_order_relaxed); } -inline SchedulingGuard::ScopedEnable::ScopedEnable() - : scheduling_disabled_depth_(0) {} +inline SchedulingGuard::ScopedEnable::ScopedEnable() { + ThreadIdentity* identity; + identity = CurrentThreadIdentityIfPresent(); + if (identity != nullptr) { + scheduling_disabled_depth_ = + identity->scheduler_state.scheduling_disabled_depth.load( + std::memory_order_relaxed); + if (scheduling_disabled_depth_ != 0) { + // The store below does not need to be compare_exchange because + // the value is never modified concurrently (only accessed). + identity->scheduler_state.scheduling_disabled_depth.store( + 0, std::memory_order_relaxed); + } + } else { + scheduling_disabled_depth_ = 0; + } +} + inline SchedulingGuard::ScopedEnable::~ScopedEnable() { - ABSL_RAW_CHECK(scheduling_disabled_depth_ == 0, "disable unused warning"); + if (scheduling_disabled_depth_ == 0) { + return; + } + ThreadIdentity* identity = CurrentThreadIdentityIfPresent(); + // itentity is guaranteed to exist, see the constructor above. + identity->scheduler_state.scheduling_disabled_depth.store( + scheduling_disabled_depth_, std::memory_order_relaxed); } } // namespace base_internal
diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index acfc15a..0c2d619 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h
@@ -146,6 +146,54 @@ // ThreadIdentity itself. PerThreadSynch per_thread_synch; + struct SchedulerState { + std::atomic<void*> bound_schedulable{nullptr}; + // Storage space for a SpinLock, which is created through a placement new to + // break a dependency cycle. + uint32_t association_lock_word; + std::atomic<int> scheduling_disabled_depth; + int potentially_blocking_depth; + uint32_t schedule_next_state; + + // When true, current thread is unlocking a mutex and actively waking a + // thread that was previously waiting, but that lock has yet more waiters. + // Used to signal to schedulers that work being woken should get an + // elevated priority. + bool waking_designated_waker; + + inline SpinLock* association_lock() { + return reinterpret_cast<SpinLock*>(&association_lock_word); + } + } scheduler_state; // Private: Reserved for use in Gloop + + // For worker threads that may not be doing any interesting user work, this + // tracks the current state of the worker. This is used to handle those + // threads differently e.g. when printing stacktraces. + // + // It should only be written to by the thread itself. + // + // Note that this is different from the mutex idle bit - threads running user + // work can be waiting but still be active. + // + // Note: not all parts of the code-base may maintain this field correctly and + // therefore this field should only be used to improve debugging/monitoring. + // + // Put it here to reuse some of the padding space. + enum class WaitState : uint8_t { + kActive = 0, + kWaitingForWork = 1, + }; + std::atomic<WaitState> wait_state; + static_assert(std::atomic<WaitState>::is_always_lock_free); + + // Add a padding such that scheduler_state is on a different cache line than + // waiter state. We use padding here, so that the size of the structure does + // not substantially grow due to the added padding. + static constexpr size_t kToBePaddedSize = + sizeof(SchedulerState) + sizeof(std::atomic<WaitState>); + static_assert(ABSL_CACHELINE_SIZE >= kToBePaddedSize); + char padding[ABSL_CACHELINE_SIZE - kToBePaddedSize]; + // Private: Reserved for absl::synchronization_internal::Waiter. struct WaiterState { alignas(void*) char data[256]; @@ -161,6 +209,10 @@ std::atomic<int> wait_start; // Ticker value when thread started waiting. std::atomic<bool> is_idle; // Has thread become idle yet? + // For tracking depth of __cxa_guard_acquire. This used to recognize heap + // allocations for function static objects. + int static_initialization_depth; + ThreadIdentity* next; };
diff --git a/absl/base/macros.h b/absl/base/macros.h index 446a445..d42ecf1 100644 --- a/absl/base/macros.h +++ b/absl/base/macros.h
@@ -108,18 +108,15 @@ #else #define ABSL_ASSERT(expr) \ (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \ - : [] { assert(false && #expr); }()) // NOLINT + : assert(false && #expr)) // NOLINT #endif // `ABSL_INTERNAL_HARDENING_ABORT()` controls how `ABSL_HARDENING_ASSERT()` // aborts the program in release mode (when NDEBUG is defined). The // implementation should abort the program as quickly as possible and ideally it // should not be possible to ignore the abort request. -#define ABSL_INTERNAL_HARDENING_ABORT() \ - do { \ - ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(); \ - ABSL_INTERNAL_UNREACHABLE_IMPL(); \ - } while (false) +#define ABSL_INTERNAL_HARDENING_ABORT() \ + ((void)ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(), ABSL_INTERNAL_UNREACHABLE_IMPL()) // ABSL_HARDENING_ASSERT() // @@ -135,7 +132,7 @@ #if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) #define ABSL_HARDENING_ASSERT(expr) \ (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \ - : [] { ABSL_INTERNAL_HARDENING_ABORT(); }()) + : ABSL_INTERNAL_HARDENING_ABORT()) #else #define ABSL_HARDENING_ASSERT(expr) ABSL_ASSERT(expr) #endif @@ -154,7 +151,7 @@ #if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) #define ABSL_HARDENING_ASSERT_SLOW(expr) \ (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \ - : [] { ABSL_INTERNAL_HARDENING_ABORT(); }()) + : ABSL_INTERNAL_HARDENING_ABORT()) #else #define ABSL_HARDENING_ASSERT_SLOW(expr) ABSL_ASSERT(expr) #endif
diff --git a/absl/base/optimization.h b/absl/base/optimization.h index b561128..1fd32c5 100644 --- a/absl/base/optimization.h +++ b/absl/base/optimization.h
@@ -217,7 +217,7 @@ #elif defined(_MSC_VER) #define ABSL_INTERNAL_UNREACHABLE_IMPL() __assume(false) #else -#define ABSL_INTERNAL_UNREACHABLE_IMPL() +#define ABSL_INTERNAL_UNREACHABLE_IMPL() ((void)0) #endif // `ABSL_UNREACHABLE()` is an unreachable statement. A program which reaches
diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index c51a19f..e6f1528 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h
@@ -96,7 +96,7 @@ absl::allocator_is_nothrow<allocator_type>::value; } static constexpr bool DefaultConstructorIsNonTrivial() { - return !absl::is_trivially_default_constructible<StorageElement>::value; + return !std::is_trivially_default_constructible<StorageElement>::value; } public: @@ -414,14 +414,14 @@ // error: call to int __builtin___sprintf_chk(etc...) // will always overflow destination buffer [-Werror] // - template <typename OuterT, typename InnerT = absl::remove_extent_t<OuterT>, + template <typename OuterT, typename InnerT = std::remove_extent_t<OuterT>, size_t InnerN = std::extent<OuterT>::value> struct StorageElementWrapper { InnerT array[InnerN]; }; using StorageElement = - absl::conditional_t<std::is_array<value_type>::value, + std::conditional_t<std::is_array<value_type>::value, StorageElementWrapper<value_type>, value_type>; static pointer AsValueType(pointer ptr) { return ptr; } @@ -458,7 +458,7 @@ }; using InlinedStorage = - absl::conditional_t<inline_elements == 0, EmptyInlinedStorage, + std::conditional_t<inline_elements == 0, EmptyInlinedStorage, NonEmptyInlinedStorage>; // Storage
diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index 1677f6a..e6cea2e 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h
@@ -614,22 +614,22 @@ // Erasure and/or insertion of elements in the function is not allowed. template <typename K, typename V, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(const flat_hash_map<K, V, H, E, A>& c, - Function&& f) { +std::decay_t<Function> c_for_each_fast(const flat_hash_map<K, V, H, E, A>& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template <typename K, typename V, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(flat_hash_map<K, V, H, E, A>& c, - Function&& f) { +std::decay_t<Function> c_for_each_fast(flat_hash_map<K, V, H, E, A>& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template <typename K, typename V, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(flat_hash_map<K, V, H, E, A>&& c, - Function&& f) { +std::decay_t<Function> c_for_each_fast(flat_hash_map<K, V, H, E, A>&& c, + Function&& f) { container_internal::ForEach(f, &c); return f; }
diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h index f802057..44af56a 100644 --- a/absl/container/flat_hash_set.h +++ b/absl/container/flat_hash_set.h
@@ -517,18 +517,20 @@ // There is no guarantees on the order of the function calls. // Erasure and/or insertion of elements in the function is not allowed. template <typename T, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(const flat_hash_set<T, H, E, A>& c, - Function&& f) { +std::decay_t<Function> c_for_each_fast(const flat_hash_set<T, H, E, A>& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template <typename T, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(flat_hash_set<T, H, E, A>& c, Function&& f) { +std::decay_t<Function> c_for_each_fast(flat_hash_set<T, H, E, A>& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template <typename T, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(flat_hash_set<T, H, E, A>&& c, Function&& f) { +std::decay_t<Function> c_for_each_fast(flat_hash_set<T, H, E, A>&& c, + Function&& f) { container_internal::ForEach(f, &c); return f; }
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index a9c2526..c1b5785 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h
@@ -192,7 +192,7 @@ // allocator doesn't do anything fancy, and there is nothing on the heap // then we know it is legal for us to simply memcpy the other vector's // inlined bytes to form our copy of its elements. - if (absl::is_trivially_copy_constructible<value_type>::value && + if (std::is_trivially_copy_constructible<value_type>::value && std::is_same<A, std::allocator<value_type>>::value && !other.storage_.GetIsAllocated()) { storage_.MemcpyFrom(other.storage_); @@ -857,7 +857,7 @@ // Assumption check: we shouldn't be told to use memcpy to implement move // assignment unless we have trivially destructible elements and an // allocator that does nothing fancy. - static_assert(absl::is_trivially_destructible<value_type>::value, ""); + static_assert(std::is_trivially_destructible<value_type>::value, ""); static_assert(std::is_same<A, std::allocator<value_type>>::value, ""); // Throw away our existing heap allocation, if any. There is no need to
diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index ed541e7..0fc77f8 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h
@@ -231,7 +231,7 @@ explicit operator Compare() const { return comp(); } template <typename T, typename U, - absl::enable_if_t< + std::enable_if_t< std::is_same<bool, compare_result_t<Compare, T, U>>::value, int> = 0> bool operator()(const T &lhs, const U &rhs) const { @@ -247,7 +247,7 @@ template < typename T, typename U, - absl::enable_if_t<std::is_convertible<compare_result_t<Compare, T, U>, + std::enable_if_t<std::is_convertible<compare_result_t<Compare, T, U>, absl::weak_ordering>::value, int> = 0> absl::weak_ordering operator()(const T &lhs, const U &rhs) const { @@ -270,7 +270,7 @@ return lhs_comp_rhs; } }; - using type = absl::conditional_t< + using type = std::conditional_t< std::is_base_of<BtreeTestOnlyCheckedCompareOptOutBase, Compare>::value, Compare, checked_compare>; }; @@ -377,7 +377,7 @@ // this, then there will be cascading compilation failures that are confusing // for users. using key_compare = - absl::conditional_t<!compare_has_valid_result_type<Compare, Key>(), + std::conditional_t<!compare_has_valid_result_type<Compare, Key>(), Compare, typename key_compare_adapter<Compare, Key>::type>; @@ -406,7 +406,7 @@ using const_reference = const value_type &; using value_compare = - absl::conditional_t<IsMap, + std::conditional_t<IsMap, map_value_compare<original_key_compare, value_type>, original_key_compare>; using is_map_container = std::integral_constant<bool, IsMap>; @@ -438,7 +438,7 @@ // This is an integral type large enough to hold as many slots as will fit a // node of TargetNodeSize bytes. using node_count_type = - absl::conditional_t<(kNodeSlotSpace / sizeof(slot_type) > + std::conditional_t<(kNodeSlotSpace / sizeof(slot_type) > (std::numeric_limits<uint8_t>::max)()), uint16_t, uint8_t>; // NOLINT }; @@ -1119,7 +1119,7 @@ using slot_type = typename params_type::slot_type; // In sets, all iterators are const. - using iterator = absl::conditional_t< + using iterator = std::conditional_t< is_map_container::value, btree_iterator<normal_node, normal_reference, normal_pointer>, btree_iterator<normal_node, const_reference, const_pointer>>; @@ -1146,7 +1146,7 @@ // const_iterator, but it specifically avoids hiding the copy constructor so // that the trivial one will be used when possible. template <typename N, typename R, typename P, - absl::enable_if_t< + std::enable_if_t< std::is_same<btree_iterator<N, R, P>, iterator>::value && std::is_same<btree_iterator, const_iterator>::value, int> = 0> @@ -1252,7 +1252,7 @@ // NOTE: the const_cast is safe because this constructor is only called by // non-const methods and the container owns the nodes. template <typename N, typename R, typename P, - absl::enable_if_t< + std::enable_if_t< std::is_same<btree_iterator<N, R, P>, const_iterator>::value && std::is_same<btree_iterator, iterator>::value, int> = 0>
diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h index 0ccf9d0..c11620d 100644 --- a/absl/container/internal/btree_container.h +++ b/absl/container/internal/btree_container.h
@@ -412,8 +412,8 @@ // `this`, it is left unmodified in `src`. template < typename T, - typename absl::enable_if_t< - absl::conjunction< + typename std::enable_if_t< + std::conjunction< std::is_same<value_type, typename T::value_type>, std::is_same<allocator_type, typename T::allocator_type>, std::is_same<typename params_type::is_map_container, @@ -431,8 +431,8 @@ template < typename T, - typename absl::enable_if_t< - absl::conjunction< + typename std::enable_if_t< + std::conjunction< std::is_same<value_type, typename T::value_type>, std::is_same<allocator_type, typename T::allocator_type>, std::is_same<typename params_type::is_map_container, @@ -474,7 +474,7 @@ typename Tree::params_type::mapped_type, M>>; template <class K, bool KValue, class M, bool MValue, typename... Dummy> using LifetimeBoundKV = - absl::conjunction<LifetimeBoundK<K, KValue, absl::void_t<Dummy...>>, + std::conjunction<LifetimeBoundK<K, KValue, absl::void_t<Dummy...>>, LifetimeBoundV<M, MValue>>; public: @@ -824,8 +824,8 @@ // Moves all elements from `src` into `this`. template < typename T, - typename absl::enable_if_t< - absl::conjunction< + typename std::enable_if_t< + std::conjunction< std::is_same<value_type, typename T::value_type>, std::is_same<allocator_type, typename T::allocator_type>, std::is_same<typename params_type::is_map_container, @@ -840,8 +840,8 @@ template < typename T, - typename absl::enable_if_t< - absl::conjunction< + typename std::enable_if_t< + std::conjunction< std::is_same<value_type, typename T::value_type>, std::is_same<allocator_type, typename T::allocator_type>, std::is_same<typename params_type::is_map_container,
diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h index 62517af..3bf0c7c 100644 --- a/absl/container/internal/common.h +++ b/absl/container/internal/common.h
@@ -58,7 +58,7 @@ using EnableIf = std::enable_if_t<Cond::value, int>; template <bool Value, class T> -using HasValue = std::conditional_t<Value, T, absl::negation<T>>; +using HasValue = std::conditional_t<Value, T, std::negation<T>>; template <class T> struct IfRRef {
diff --git a/absl/container/internal/common_policy_traits.h b/absl/container/internal/common_policy_traits.h index 86e038e..3b0d505 100644 --- a/absl/container/internal/common_policy_traits.h +++ b/absl/container/internal/common_policy_traits.h
@@ -81,7 +81,7 @@ // Note: we use remove_const_t so that the two overloads have different args // in the case of sets with explicitly const value_types. template <class P = Policy> - static auto element(absl::remove_const_t<slot_type>* slot) + static auto element(std::remove_const_t<slot_type>* slot) -> decltype(P::element(slot)) { return P::element(slot); }
diff --git a/absl/container/internal/compressed_tuple.h b/absl/container/internal/compressed_tuple.h index 2dd8d6c..9d5c055 100644 --- a/absl/container/internal/compressed_tuple.h +++ b/absl/container/internal/compressed_tuple.h
@@ -166,7 +166,7 @@ template <class... Ts, class... Vs> struct TupleMoveConstructible<true, CompressedTuple<Ts...>, Vs...> : std::integral_constant< - bool, absl::conjunction< + bool, std::conjunction< TupleElementMoveConstructible<Ts, Vs&&>...>::value> {}; template <typename T> @@ -227,11 +227,11 @@ : CompressedTuple::CompressedTupleImpl(absl::in_place, base...) {} template <typename First, typename... Vs, - absl::enable_if_t< - absl::conjunction< + std::enable_if_t< + std::conjunction< // Ensure we are not hiding default copy/move constructors. - absl::negation<std::is_same<void(CompressedTuple), - void(absl::decay_t<First>)>>, + std::negation<std::is_same<void(CompressedTuple), + void(std::decay_t<First>)>>, internal_compressed_tuple::TupleItemsMoveConstructible< CompressedTuple<Ts...>, First, Vs...>>::value, bool> = true>
diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h index 47064a7..dcf0bd2 100644 --- a/absl/container/internal/container_memory.h +++ b/absl/container/internal/container_memory.h
@@ -350,11 +350,11 @@ ~map_slot_type() = delete; using value_type = std::pair<const K, V>; using mutable_value_type = - std::pair<absl::remove_const_t<K>, absl::remove_const_t<V>>; + std::pair<std::remove_const_t<K>, std::remove_const_t<V>>; value_type value; mutable_value_type mutable_value; - absl::remove_const_t<K> key; + std::remove_const_t<K> key; }; template <class K, class V> @@ -362,7 +362,7 @@ using slot_type = map_slot_type<K, V>; using value_type = std::pair<const K, V>; using mutable_value_type = - std::pair<absl::remove_const_t<K>, absl::remove_const_t<V>>; + std::pair<std::remove_const_t<K>, std::remove_const_t<V>>; private: static void emplace(slot_type* slot) {
diff --git a/absl/container/internal/hash_policy_traits.h b/absl/container/internal/hash_policy_traits.h index 82eed2a..7f5f892 100644 --- a/absl/container/internal/hash_policy_traits.h +++ b/absl/container/internal/hash_policy_traits.h
@@ -38,7 +38,7 @@ private: struct ReturnKey { template <class Key, - absl::enable_if_t<std::is_lvalue_reference<Key>::value, int> = 0> + std::enable_if_t<std::is_lvalue_reference<Key>::value, int> = 0> static key_type& Impl(Key&& k, int) { return *std::launder( const_cast<key_type*>(std::addressof(std::forward<Key>(k))));
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index c7b709f..42878ac 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h
@@ -79,7 +79,7 @@ template <typename A, bool IsTriviallyDestructible = - absl::is_trivially_destructible<ValueType<A>>::value && + std::is_trivially_destructible<ValueType<A>>::value && std::is_same<A, std::allocator<ValueType<A>>>::value> struct DestroyAdapter; @@ -288,14 +288,14 @@ struct ElementwiseSwapPolicy {}; struct ElementwiseConstructPolicy {}; - using MoveAssignmentPolicy = absl::conditional_t< + using MoveAssignmentPolicy = std::conditional_t< // Fast path: if the value type can be trivially move assigned and // destroyed, and we know the allocator doesn't do anything fancy, then // it's safe for us to simply adopt the contents of the storage for // `other` and remove its own reference to them. It's as if we had // individually move-assigned each value and then destroyed the original. - absl::conjunction<absl::is_trivially_move_assignable<ValueType<A>>, - absl::is_trivially_destructible<ValueType<A>>, + std::conjunction<std::is_trivially_move_assignable<ValueType<A>>, + std::is_trivially_destructible<ValueType<A>>, std::is_same<A, std::allocator<ValueType<A>>>>::value, MemcpyPolicy, // Otherwise we use move assignment if possible. If not, we simulate @@ -304,21 +304,21 @@ // Note that this is in contrast to e.g. std::vector and std::optional, // which are themselves not move-assignable when their contained type is // not. - absl::conditional_t<IsMoveAssignOk<A>::value, ElementwiseAssignPolicy, + std::conditional_t<IsMoveAssignOk<A>::value, ElementwiseAssignPolicy, ElementwiseConstructPolicy>>; // The policy to be used specifically when swapping inlined elements. - using SwapInlinedElementsPolicy = absl::conditional_t< + using SwapInlinedElementsPolicy = std::conditional_t< // Fast path: if the value type can be trivially relocated, and we // know the allocator doesn't do anything fancy, then it's safe for us // to simply swap the bytes in the inline storage. It's as if we had // relocated the first vector's elements into temporary storage, // relocated the second's elements into the (now-empty) first's, // and then relocated from temporary storage into the second. - absl::conjunction<absl::is_trivially_relocatable<ValueType<A>>, + std::conjunction<absl::is_trivially_relocatable<ValueType<A>>, std::is_same<A, std::allocator<ValueType<A>>>>::value, MemcpyPolicy, - absl::conditional_t<IsSwapOk<A>::value, ElementwiseSwapPolicy, + std::conditional_t<IsSwapOk<A>::value, ElementwiseSwapPolicy, ElementwiseConstructPolicy>>; static SizeType<A> NextCapacity(SizeType<A> current_capacity) { @@ -348,7 +348,7 @@ // Fast path: if no destructors need to be run and we know the allocator // doesn't do anything fancy, then all we need to do is deallocate (and // maybe not even that). - if (absl::is_trivially_destructible<ValueType<A>>::value && + if (std::is_trivially_destructible<ValueType<A>>::value && std::is_same<A, std::allocator<ValueType<A>>>::value) { DeallocateIfAllocated(); return; @@ -507,11 +507,11 @@ // First case above absl::is_trivially_relocatable<V>::value || // Second case above - (absl::is_trivially_move_assignable<V>::value && - absl::is_trivially_destructible<V>::value) || + (std::is_trivially_move_assignable<V>::value && + std::is_trivially_destructible<V>::value) || // Third case above - (absl::is_trivially_copy_constructible<V>::value || - absl::is_trivially_copy_assignable<V>::value)))); + (std::is_trivially_copy_constructible<V>::value || + std::is_trivially_copy_assignable<V>::value)))); } GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); @@ -596,7 +596,7 @@ // Fast path: if the value type is trivially copy constructible and we know // the allocator doesn't do anything fancy, then we know it is legal for us to // simply memcpy the other vector's elements. - if (absl::is_trivially_copy_constructible<ValueType<A>>::value && + if (std::is_trivially_copy_constructible<ValueType<A>>::value && std::is_same<A, std::allocator<ValueType<A>>>::value) { std::memcpy(reinterpret_cast<char*>(dst), reinterpret_cast<const char*>(src), n * sizeof(ValueType<A>));
diff --git a/absl/container/internal/layout.h b/absl/container/internal/layout.h index 58c8d4f..f49d272 100644 --- a/absl/container/internal/layout.h +++ b/absl/container/internal/layout.h
@@ -261,7 +261,7 @@ // Does `Ts...` contain `T`? template <class T, class... Ts> -using Contains = absl::disjunction<std::is_same<T, Ts>...>; +using Contains = std::disjunction<std::is_same<T, Ts>...>; template <class From, class To> using CopyConst = @@ -352,7 +352,7 @@ absl::index_sequence<OffsetSeq...>> { private: static_assert(sizeof...(Elements) > 0, "At least one field is required"); - static_assert(absl::conjunction<IsLegalElementType<Elements>...>::value, + static_assert(std::conjunction<IsLegalElementType<Elements>...>::value, "Invalid element type (see IsLegalElementType)"); static_assert(sizeof...(StaticSizeSeq) <= sizeof...(Elements), "Too many static sizes specified");
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h index 20b622b..25ed119 100644 --- a/absl/container/internal/raw_hash_map.h +++ b/absl/container/internal/raw_hash_map.h
@@ -88,7 +88,7 @@ typename Policy::mapped_type, V>>; template <class K, bool KValue, class V, bool VValue, typename... Dummy> using LifetimeBoundKV = - absl::conjunction<LifetimeBoundK<K, KValue, absl::void_t<Dummy...>>, + std::conjunction<LifetimeBoundK<K, KValue, absl::void_t<Dummy...>>, LifetimeBoundV<V, VValue>>; public:
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index 15d072a..048d2ee 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc
@@ -1528,6 +1528,10 @@ if (common.capacity() == 1) { if (common.empty()) { IncrementSmallSizeNonSoo(common, policy); + if (common.has_infoz()) { + common.infoz().RecordInsertMiss(get_hash(common.seed().seed()), + /*distance_from_desired=*/0); + } return {SooControl(), common.slot_array()}; } else { return Grow1To3AndPrepareInsert(common, policy, get_hash);
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 1a9fa1d..805ea30 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h
@@ -1930,7 +1930,7 @@ // An enabler for insert(T&&): T must be convertible to init_type or be the // same as [cv] value_type [ref]. template <class T> - using Insertable = absl::disjunction< + using Insertable = std::disjunction< std::is_same<absl::remove_cvref_t<reference>, absl::remove_cvref_t<T>>, std::is_convertible<T, init_type>>; template <class T> @@ -1970,9 +1970,9 @@ using iterator_category = std::forward_iterator_tag; using value_type = typename raw_hash_set::value_type; using reference = - absl::conditional_t<PolicyTraits::constant_iterators::value, + std::conditional_t<PolicyTraits::constant_iterators::value, const value_type&, value_type&>; - using pointer = absl::remove_reference_t<reference>*; + using pointer = std::remove_reference_t<reference>*; using difference_type = typename raw_hash_set::difference_type; iterator() {}
diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc index 2e9a81d..71feffe 100644 --- a/absl/container/internal/raw_hash_set_benchmark.cc +++ b/absl/container/internal/raw_hash_set_benchmark.cc
@@ -244,6 +244,30 @@ } BENCHMARK(BM_EraseEmplace)->Arg(1)->Arg(2)->Arg(4)->Arg(8)->Arg(16)->Arg(100); +void BM_EraseEmplaceString(benchmark::State& state) { + StringTable t; + int64_t size = state.range(0); + for (int64_t i = 0; i < size; ++i) { + std::string s = std::to_string(i); + t.emplace(s, s); + } + while (state.KeepRunningBatch(size)) { + for (int64_t i = 0; i < size; ++i) { + benchmark::DoNotOptimize(t); + std::string s = std::to_string(i); + t.erase(s); + t.emplace(s, s); + } + } +} +BENCHMARK(BM_EraseEmplaceString) + ->Arg(1) + ->Arg(2) + ->Arg(4) + ->Arg(8) + ->Arg(16) + ->Arg(100); + void BM_EndComparison(benchmark::State& state) { StringTable t = {{"a", "a"}, {"b", "b"}}; auto it = t.begin();
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 9c33e3c..952399a 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc
@@ -2878,8 +2878,9 @@ } } -std::vector<const HashtablezInfo*> SampleSooMutation( - absl::FunctionRef<void(SooInt32Table&)> mutate_table) { +template <typename IntTableType> +std::vector<const HashtablezInfo*> SampleTableMutation( + absl::FunctionRef<void(IntTableType&)> mutate_table) { // Enable the feature even if the prod default is off. SetSamplingRateTo1Percent(); @@ -2892,7 +2893,7 @@ ++start_size; }); - std::vector<SooInt32Table> tables; + std::vector<IntTableType> tables; for (int i = 0; i < 1000000; ++i) { tables.emplace_back(); mutate_table(tables.back()); @@ -2911,6 +2912,16 @@ return infos; } +std::vector<const HashtablezInfo*> SampleSooMutation( + absl::FunctionRef<void(SooInt32Table&)> mutate_table) { + return SampleTableMutation<SooInt32Table>(mutate_table); +} + +std::vector<const HashtablezInfo*> SampleNonSooMutation( + absl::FunctionRef<void(NonSooIntTable&)> mutate_table) { + return SampleTableMutation<NonSooIntTable>(mutate_table); +} + TEST(RawHashSamplerTest, SooTableInsertToEmpty) { if (SooInt32Table().capacity() != SooCapacity()) { CHECK_LT(sizeof(void*), 8) << "missing SOO coverage"; @@ -2956,6 +2967,22 @@ // instance. } +TEST(RawHashSamplerTest, NonSooTableRepeatedInsertEraseCountSizeRight) { + ASSERT_EQ(NonSooIntTable().capacity(), 0); + std::vector<const HashtablezInfo*> infos = + SampleNonSooMutation([](NonSooIntTable& t) { + for (int i = 0; i < 10; ++i) { + t.insert(1); + t.erase(1); + } + }); + for (const HashtablezInfo* info : infos) { + EXPECT_EQ(info->soo_capacity, 0); + ASSERT_EQ(info->capacity, 1); + ASSERT_EQ(info->size, 0); + } +} + // Verifies that copy-constructing or copy-assigning an SOO table does not // incorrectly trigger new sampling evaluations. TEST(RawHashSamplerTest, SooTableCopyDoesNotOversample) {
diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index 3ceef32..6fe1d9f 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h
@@ -604,22 +604,22 @@ // Erasure and/or insertion of elements in the function is not allowed. template <typename K, typename V, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(const node_hash_map<K, V, H, E, A>& c, - Function&& f) { +std::decay_t<Function> c_for_each_fast(const node_hash_map<K, V, H, E, A>& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template <typename K, typename V, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(node_hash_map<K, V, H, E, A>& c, - Function&& f) { +std::decay_t<Function> c_for_each_fast(node_hash_map<K, V, H, E, A>& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template <typename K, typename V, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(node_hash_map<K, V, H, E, A>&& c, - Function&& f) { +std::decay_t<Function> c_for_each_fast(node_hash_map<K, V, H, E, A>&& c, + Function&& f) { container_internal::ForEach(f, &c); return f; }
diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index 17aa763..4d58098 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h
@@ -512,18 +512,20 @@ // There is no guarantees on the order of the function calls. // Erasure and/or insertion of elements in the function is not allowed. template <typename T, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(const node_hash_set<T, H, E, A>& c, - Function&& f) { +std::decay_t<Function> c_for_each_fast(const node_hash_set<T, H, E, A>& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template <typename T, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(node_hash_set<T, H, E, A>& c, Function&& f) { +std::decay_t<Function> c_for_each_fast(node_hash_set<T, H, E, A>& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template <typename T, typename H, typename E, typename A, typename Function> -decay_t<Function> c_for_each_fast(node_hash_set<T, H, E, A>&& c, Function&& f) { +std::decay_t<Function> c_for_each_fast(node_hash_set<T, H, E, A>&& c, + Function&& f) { container_internal::ForEach(f, &c); return f; }
diff --git a/absl/debugging/internal/demangle_rust.h b/absl/debugging/internal/demangle_rust.h index 94a9aec..299c17a 100644 --- a/absl/debugging/internal/demangle_rust.h +++ b/absl/debugging/internal/demangle_rust.h
@@ -32,6 +32,14 @@ // DemangleRustSymbolEncoding is async-signal-safe and runs in bounded C++ // call-stack space. It is suitable for symbolizing stack traces in a signal // handler. +// +// Note that this demangler purposefully omits some details: generic argument +// lists become `<>`, function types `fn...`, and long tuples `(t, u, v, ...)`. +// This simplification suits crash backtracing, where the signal handler must +// not `malloc`, and the human troubleshooter won't want the `file:line` blame +// drowned in generic arguments. Applications better served by a freely +// allocating detailed demangler might prefer to use Rust's own `rustc-demangle` +// crate. bool DemangleRustSymbolEncoding(const char* mangled, char* out, size_t out_size);
diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h index bd2c9c7..a994dba 100644 --- a/absl/debugging/internal/symbolize.h +++ b/absl/debugging/internal/symbolize.h
@@ -78,46 +78,6 @@ ABSL_NAMESPACE_BEGIN namespace debugging_internal { -// Legacy stateless symbol decorator API. Will be removed soon. -struct SymbolDecoratorArgs { - // The program counter we are getting symbolic name for. - const void *pc; - // 0 for main executable, load address for shared libraries. - ptrdiff_t relocation; - // Read-only file descriptor for ELF image covering "pc", - // or -1 if no such ELF image exists in /proc/self/maps. - int fd; - // Output buffer, size. - // Note: the buffer may not be empty -- default symbolizer may have already - // produced some output, and earlier decorators may have adorned it in - // some way. You are free to replace or augment the contents (within the - // symbol_buf_size limit). - char *const symbol_buf; - size_t symbol_buf_size; - // Temporary scratch space, size. - // Use that space in preference to allocating your own stack buffer to - // conserve stack. - char *const tmp_buf; - size_t tmp_buf_size; - // User-provided argument - void* arg; -}; -using LegacySymbolDecorator = void (*)(const SymbolDecoratorArgs *); - -// Installs a function-pointer as a decorator. Returns a value less than zero -// if the system cannot install the decorator. Otherwise, returns a unique -// identifier corresponding to the decorator. This identifier can be used to -// uninstall the decorator - See RemoveSymbolDecorator() below. -int InstallSymbolDecorator(LegacySymbolDecorator decorator, void* arg); - -// Removes a previously installed function-pointer decorator. Parameter "ticket" -// is the return-value from calling InstallSymbolDecorator(). -bool RemoveSymbolDecorator(int ticket); - -// Remove all installed decorators. Returns true if successful, false if -// symbolization is currently in progress. -bool RemoveAllSymbolDecorators(); - class SymbolDecorator; class SymbolDecoratorDeleter {
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index 5630cc2..14b23c1 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc
@@ -146,17 +146,6 @@ // pointers and the first one is the function's entry. const size_t kFunctionDescriptorSize = sizeof(void *) * 2; -const int kMaxDecorators = 10; // Seems like a reasonable upper limit. - -struct InstalledSymbolDecorator { - LegacySymbolDecorator fn; - void *arg; - int ticket; -}; - -int g_num_decorators; -InstalledSymbolDecorator g_decorators[kMaxDecorators]; - std::atomic<SymbolDecorator::Factory*> g_decorator_factory = nullptr; struct FileMappingHint { @@ -166,16 +155,6 @@ const char *filename; }; -// Protects g_decorators. -// We are using SpinLock and not a Mutex here, because we may be called -// from inside Mutex::Lock itself, and it prohibits recursive calls. -// This happens in e.g. base/stacktrace_syscall_unittest. -// Moreover, we are using only try_lock(), if the decorator list -// is being modified (is busy), we skip all decorators, and possibly -// loose some info. Sorry, that's the best we could do. -ABSL_CONST_INIT absl::base_internal::SpinLock g_decorators_mu( - absl::base_internal::SCHEDULE_KERNEL_ONLY); - const int kMaxFileMappingHints = 8; int g_num_file_mapping_hints; FileMappingHint g_file_mapping_hints[kMaxFileMappingHints]; @@ -1507,7 +1486,6 @@ const char *Symbolizer::GetUncachedSymbol(const void *pc) { ObjFile *const obj = FindObjFile(pc, 1); ptrdiff_t relocation = 0; - int fd = -1; if (obj != nullptr) { if (MaybeInitializeObjFile(obj, decorator_factory_)) { const size_t start_addr = reinterpret_cast<size_t>(obj->start_addr); @@ -1549,7 +1527,6 @@ } } - fd = obj->fd; if (GetSymbolFromObjectFile(*obj, pc, relocation, symbol_buf_, sizeof(symbol_buf_), tmp_buf_, sizeof(tmp_buf_)) == SYMBOL_FOUND) { @@ -1574,18 +1551,6 @@ #endif } - if (g_decorators_mu.try_lock()) { - if (g_num_decorators > 0) { - SymbolDecoratorArgs decorator_args = { - pc, relocation, fd, symbol_buf_, sizeof(symbol_buf_), - tmp_buf_, sizeof(tmp_buf_), nullptr}; - for (int i = 0; i < g_num_decorators; ++i) { - decorator_args.arg = g_decorators[i].arg; - g_decorators[i].fn(&decorator_args); - } - } - g_decorators_mu.unlock(); - } if (obj != nullptr && obj->decorator != nullptr) { obj->decorator->Decorate(pc, relocation, symbol_buf_, sizeof(symbol_buf_), tmp_buf_, sizeof(tmp_buf_)); @@ -1633,55 +1598,6 @@ #endif } -bool RemoveAllSymbolDecorators() { - SetSymbolDecoratorFactory(nullptr); - - if (!g_decorators_mu.try_lock()) { - // Someone else is using decorators. Get out. - return false; - } - g_num_decorators = 0; - g_decorators_mu.unlock(); - return true; -} - -bool RemoveSymbolDecorator(int ticket) { - if (!g_decorators_mu.try_lock()) { - // Someone else is using decorators. Get out. - return false; - } - for (int i = 0; i < g_num_decorators; ++i) { - if (g_decorators[i].ticket == ticket) { - while (i < g_num_decorators - 1) { - g_decorators[i] = g_decorators[i + 1]; - ++i; - } - g_num_decorators = i; - break; - } - } - g_decorators_mu.unlock(); - return true; // Decorator is known to be removed. -} - -int InstallSymbolDecorator(LegacySymbolDecorator decorator, void *arg) { - static int ticket = 0; - - if (!g_decorators_mu.try_lock()) { - // Someone else is using decorators. Get out. - return -2; - } - int ret = ticket; - if (g_num_decorators >= kMaxDecorators) { - ret = -1; - } else { - g_decorators[g_num_decorators] = {decorator, arg, ticket++}; - ++g_num_decorators; - } - g_decorators_mu.unlock(); - return ret; -} - SymbolDecorator::Factory* SetSymbolDecoratorFactory( SymbolDecorator::Factory* factory) { return g_decorator_factory.exchange(factory, std::memory_order_acq_rel);
diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index 5e1e65d..5eb68e0 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc
@@ -375,48 +375,6 @@ } } -// Appends string(*args->arg) to args->symbol_buf. -static void DummySymbolDecorator( - const absl::debugging_internal::SymbolDecoratorArgs *args) { - std::string *message = static_cast<std::string *>(args->arg); - strncat(args->symbol_buf, message->c_str(), - args->symbol_buf_size - strlen(args->symbol_buf) - 1); -} - -TEST(Symbolize, InstallAndRemoveSymbolDecorators) { - int ticket_a; - std::string a_message("a"); - EXPECT_GE(ticket_a = absl::debugging_internal::InstallSymbolDecorator( - DummySymbolDecorator, &a_message), - 0); - - int ticket_b; - std::string b_message("b"); - EXPECT_GE(ticket_b = absl::debugging_internal::InstallSymbolDecorator( - DummySymbolDecorator, &b_message), - 0); - - int ticket_c; - std::string c_message("c"); - EXPECT_GE(ticket_c = absl::debugging_internal::InstallSymbolDecorator( - DummySymbolDecorator, &c_message), - 0); - - // Use addresses 4 and 8 here to ensure that we always use valid addresses - // even on systems that require instructions to be 32-bit aligned. - char *address = reinterpret_cast<char *>(4); - EXPECT_STREQ("abc", TrySymbolize(address)); - - EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_b)); - - EXPECT_STREQ("ac", TrySymbolize(address + 4)); - - // Cleanup: remove all remaining decorators so other stack traces don't - // get mystery "ac" decoration. - EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_a)); - EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_c)); -} - template <char C> class TestSymbolDecorator final : public absl::debugging_internal::SymbolDecorator {
diff --git a/absl/debugging/symbolize_unimplemented.inc b/absl/debugging/symbolize_unimplemented.inc index 1ba5bf4..b0faa9d 100644 --- a/absl/debugging/symbolize_unimplemented.inc +++ b/absl/debugging/symbolize_unimplemented.inc
@@ -21,10 +21,6 @@ namespace debugging_internal { -int InstallSymbolDecorator(LegacySymbolDecorator, void*) { return -1; } -bool RemoveSymbolDecorator(int) { return false; } -bool RemoveAllSymbolDecorators(void) { return false; } - SymbolDecorator::Factory* SetSymbolDecoratorFactory(SymbolDecorator::Factory*) { return nullptr; }
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 285d842..e26b2ad 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h
@@ -608,7 +608,7 @@ *value = ReadOneBool(); } template <typename T, - absl::enable_if_t<flags_internal::StorageKind<T>() == + std::enable_if_t<flags_internal::StorageKind<T>() == FlagValueStorageKind::kOneWordAtomic, int> = 0> void Read(T* value) const ABSL_LOCKS_EXCLUDED(DataGuard()) {
diff --git a/absl/functional/any_invocable.h b/absl/functional/any_invocable.h index 6dd72b6..4ba4fe3 100644 --- a/absl/functional/any_invocable.h +++ b/absl/functional/any_invocable.h
@@ -191,7 +191,7 @@ // Upon construction, `*this` is only empty if `f` is a function pointer or // member pointer type and is null, or if `f` is an `AnyInvocable` that is // empty. - template <class F, typename = absl::enable_if_t< + template <class F, typename = std::enable_if_t< internal_any_invocable::CanConvert<Sig, F>::value>> AnyInvocable(F&& f) // NOLINT : Impl(internal_any_invocable::ConversionConstruct(), @@ -206,25 +206,25 @@ // absl::in_place_type<PossiblyImmovableType>, arg1, arg2); // template <class T, class... Args, - typename = absl::enable_if_t< + typename = std::enable_if_t< internal_any_invocable::CanEmplace<Sig, T, Args...>::value>> explicit AnyInvocable(absl::in_place_type_t<T>, Args&&... args) - : Impl(absl::in_place_type<absl::decay_t<T>>, + : Impl(absl::in_place_type<std::decay_t<T>>, std::forward<Args>(args)...) { - static_assert(std::is_same<T, absl::decay_t<T>>::value, + static_assert(std::is_same<T, std::decay_t<T>>::value, "The explicit template argument of in_place_type is required " "to be an unqualified object type."); } // Overload of the above constructor to support list-initialization. template <class T, class U, class... Args, - typename = absl::enable_if_t<internal_any_invocable::CanEmplace< + typename = std::enable_if_t<internal_any_invocable::CanEmplace< Sig, T, std::initializer_list<U>&, Args...>::value>> explicit AnyInvocable(absl::in_place_type_t<T>, std::initializer_list<U> ilist, Args&&... args) - : Impl(absl::in_place_type<absl::decay_t<T>>, ilist, + : Impl(absl::in_place_type<std::decay_t<T>>, ilist, std::forward<Args>(args)...) { - static_assert(std::is_same<T, absl::decay_t<T>>::value, + static_assert(std::is_same<T, std::decay_t<T>>::value, "The explicit template argument of in_place_type is required " "to be an unqualified object type."); } @@ -248,7 +248,7 @@ // Upon assignment, `*this` is only empty if `f` is a function pointer or // member pointer type and is null, or if `f` is an `AnyInvocable` that is // empty. - template <class F, typename = absl::enable_if_t< + template <class F, typename = std::enable_if_t< internal_any_invocable::CanAssign<Sig, F>::value>> AnyInvocable& operator=(F&& f) { *this = AnyInvocable(std::forward<F>(f)); @@ -260,7 +260,7 @@ // `AnyInvocable` instance. template < class F, - typename = absl::enable_if_t< + typename = std::enable_if_t< internal_any_invocable::CanAssignReferenceWrapper<Sig, F>::value>> AnyInvocable& operator=(std::reference_wrapper<F> f) noexcept { *this = AnyInvocable(f);
diff --git a/absl/functional/any_invocable_test.cc b/absl/functional/any_invocable_test.cc index 7ddfcab..9c5068a 100644 --- a/absl/functional/any_invocable_test.cc +++ b/absl/functional/any_invocable_test.cc
@@ -41,7 +41,7 @@ template <class T> struct Wrapper { template <class U, - class = absl::enable_if_t<std::is_convertible<U, T>::value>> + class = std::enable_if_t<std::is_convertible<U, T>::value>> Wrapper(U&&); // NOLINT }; @@ -58,7 +58,7 @@ struct QualifiersForThisImpl { static_assert(std::is_object<This>::value, ""); using type = - absl::conditional_t<std::is_const<Qualifiers>::value, const This, This>&; + std::conditional_t<std::is_const<Qualifiers>::value, const This, This>&; }; template <class Qualifiers, class This> @@ -69,7 +69,7 @@ struct QualifiersForThisImpl<Qualifiers&&, This> { static_assert(std::is_object<This>::value, ""); using type = - absl::conditional_t<std::is_const<Qualifiers>::value, const This, This>&&; + std::conditional_t<std::is_const<Qualifiers>::value, const This, This>&&; }; template <class Qualifiers, class This> @@ -84,38 +84,38 @@ template <class T, class R, class... P> struct GiveQualifiersToFunImpl<T, R(P...)> { using type = - absl::conditional_t<std::is_const<T>::value, R(P...) const, R(P...)>; + std::conditional_t<std::is_const<T>::value, R(P...) const, R(P...)>; }; template <class T, class R, class... P> struct GiveQualifiersToFunImpl<T&, R(P...)> { using type = - absl::conditional_t<std::is_const<T>::value, R(P...) const&, R(P...)&>; + std::conditional_t<std::is_const<T>::value, R(P...) const&, R(P...)&>; }; template <class T, class R, class... P> struct GiveQualifiersToFunImpl<T&&, R(P...)> { using type = - absl::conditional_t<std::is_const<T>::value, R(P...) const&&, R(P...) &&>; + std::conditional_t<std::is_const<T>::value, R(P...) const&&, R(P...) &&>; }; template <class T, class R, class... P> struct GiveQualifiersToFunImpl<T, R(P...) noexcept> { - using type = absl::conditional_t<std::is_const<T>::value, + using type = std::conditional_t<std::is_const<T>::value, R(P...) const noexcept, R(P...) noexcept>; }; template <class T, class R, class... P> struct GiveQualifiersToFunImpl<T&, R(P...) noexcept> { using type = - absl::conditional_t<std::is_const<T>::value, R(P...) const & noexcept, + std::conditional_t<std::is_const<T>::value, R(P...) const & noexcept, R(P...) & noexcept>; }; template <class T, class R, class... P> struct GiveQualifiersToFunImpl<T&&, R(P...) noexcept> { using type = - absl::conditional_t<std::is_const<T>::value, R(P...) const && noexcept, + std::conditional_t<std::is_const<T>::value, R(P...) const && noexcept, R(P...) && noexcept>; }; @@ -367,14 +367,14 @@ } using CompatibleAnyInvocableFunType = - absl::conditional_t<std::is_rvalue_reference<Qual>::value, + std::conditional_t<std::is_rvalue_reference<Qual>::value, GiveQualifiersToFun<const _&&, UnqualifiedFunType>, GiveQualifiersToFun<const _&, UnqualifiedFunType>>; using CompatibleAnyInvType = AnyInvocable<CompatibleAnyInvocableFunType>; using IncompatibleInvocable = - absl::conditional_t<std::is_rvalue_reference<Qual>::value, + std::conditional_t<std::is_rvalue_reference<Qual>::value, GiveQualifiersToFun<_&, UnqualifiedFunType>(_::*), GiveQualifiersToFun<_&&, UnqualifiedFunType>(_::*)>; }; @@ -1284,7 +1284,7 @@ // Just like plain functors, it should work fine to use an AnyInvocable that // returns the non-moveable type. using UnqualifiedFun = - absl::conditional_t<TypeParam::kIsNoexcept, Result() noexcept, Result()>; + std::conditional_t<TypeParam::kIsNoexcept, Result() noexcept, Result()>; using Fun = GiveQualifiersToFun<typename TypeParam::Qualifiers, UnqualifiedFun>; @@ -1364,7 +1364,7 @@ // Just like plain functors, it should work fine to use an AnyInvocable that // returns the non-moveable type. using UnqualifiedFun = - absl::conditional_t<TypeParam::kIsNoexcept, Result() noexcept, Result()>; + std::conditional_t<TypeParam::kIsNoexcept, Result() noexcept, Result()>; using Fun = GiveQualifiersToFun<typename TypeParam::Qualifiers, UnqualifiedFun>;
diff --git a/absl/functional/internal/any_invocable.h b/absl/functional/internal/any_invocable.h index 597c210..66168c6 100644 --- a/absl/functional/internal/any_invocable.h +++ b/absl/functional/internal/any_invocable.h
@@ -611,39 +611,39 @@ /*SFINAE constraints for the conversion-constructor.*/ template <class Sig, class F, - class = absl::enable_if_t< + class = std::enable_if_t< !std::is_same<RemoveCVRef<F>, AnyInvocable<Sig>>::value>> using CanConvert = TrueAlias< - absl::enable_if_t<!IsInPlaceType<RemoveCVRef<F>>::value>, - absl::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>, - absl::enable_if_t< + std::enable_if_t<!IsInPlaceType<RemoveCVRef<F>>::value>, + std::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>, + std::enable_if_t< Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept<F>::value>, - absl::enable_if_t<std::is_constructible<absl::decay_t<F>, F>::value>>; + std::enable_if_t<std::is_constructible<std::decay_t<F>, F>::value>>; /*SFINAE constraints for the std::in_place constructors.*/ template <class Sig, class F, class... Args> using CanEmplace = TrueAlias< - absl::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>, - absl::enable_if_t< + std::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>, + std::enable_if_t< Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept<F>::value>, - absl::enable_if_t<std::is_constructible<absl::decay_t<F>, Args...>::value>>; + std::enable_if_t<std::is_constructible<std::decay_t<F>, Args...>::value>>; /*SFINAE constraints for the conversion-assign operator.*/ template <class Sig, class F, - class = absl::enable_if_t< + class = std::enable_if_t< !std::is_same<RemoveCVRef<F>, AnyInvocable<Sig>>::value>> using CanAssign = TrueAlias< - absl::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>, - absl::enable_if_t< + std::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>, + std::enable_if_t< Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept<F>::value>, - absl::enable_if_t<std::is_constructible<absl::decay_t<F>, F>::value>>; + std::enable_if_t<std::is_constructible<std::decay_t<F>, F>::value>>; /*SFINAE constraints for the reference-wrapper conversion-assign operator.*/ template <class Sig, class F> using CanAssignReferenceWrapper = TrueAlias< - absl::enable_if_t< + std::enable_if_t< Impl<Sig>::template CallIsValid<std::reference_wrapper<F>>::value>, - absl::enable_if_t<Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept< + std::enable_if_t<Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept< std::reference_wrapper<F>>::value>>; // The constraint for checking whether or not a call meets the noexcept @@ -657,17 +657,17 @@ // don't treat non-moveable result types correctly. For example this was the // case in libc++ before commit c3a24882 (2022-05). #define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_true(inv_quals) \ - absl::enable_if_t<absl::disjunction< \ + std::enable_if_t<std::disjunction< \ std::is_nothrow_invocable_r< \ - ReturnType, UnwrapStdReferenceWrapper<absl::decay_t<F>> inv_quals, \ + ReturnType, UnwrapStdReferenceWrapper<std::decay_t<F>> inv_quals, \ P...>, \ std::conjunction< \ std::is_nothrow_invocable< \ - UnwrapStdReferenceWrapper<absl::decay_t<F>> inv_quals, P...>, \ + UnwrapStdReferenceWrapper<std::decay_t<F>> inv_quals, P...>, \ std::is_same< \ ReturnType, \ std::invoke_result_t< \ - UnwrapStdReferenceWrapper<absl::decay_t<F>> inv_quals, \ + UnwrapStdReferenceWrapper<std::decay_t<F>> inv_quals, \ P...>>>>::value> #define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_false(inv_quals) @@ -696,11 +696,11 @@ \ /*SFINAE constraint to check if F is invocable with the proper signature*/ \ template <class F> \ - using CallIsValid = TrueAlias<absl::enable_if_t<absl::disjunction< \ - std::is_invocable_r<ReturnType, absl::decay_t<F> inv_quals, P...>, \ + using CallIsValid = TrueAlias<std::enable_if_t<std::disjunction< \ + std::is_invocable_r<ReturnType, std::decay_t<F> inv_quals, P...>, \ std::is_same< \ ReturnType, \ - std::invoke_result_t<absl::decay_t<F> inv_quals, P...>>>::value>>; \ + std::invoke_result_t<std::decay_t<F> inv_quals, P...>>>::value>>; \ \ /*SFINAE constraint to check if F is nothrow-invocable when necessary*/ \ template <class F> \ @@ -723,7 +723,7 @@ /*Forward along the in-place construction parameters.*/ \ template <class T, class... Args> \ explicit Impl(absl::in_place_type_t<T>, Args&&... args) \ - : Core(absl::in_place_type<absl::decay_t<T> inv_quals>, \ + : Core(absl::in_place_type<std::decay_t<T> inv_quals>, \ std::forward<Args>(args)...) {} \ \ /*Raises a fatal error when the AnyInvocable is invoked after a move*/ \
diff --git a/absl/functional/internal/front_binder.h b/absl/functional/internal/front_binder.h index 62f373f..28a15ce 100644 --- a/absl/functional/internal/front_binder.h +++ b/absl/functional/internal/front_binder.h
@@ -84,7 +84,7 @@ }; template <class F, class... BoundArgs> -using bind_front_t = FrontBinder<decay_t<F>, absl::decay_t<BoundArgs>...>; +using bind_front_t = FrontBinder<std::decay_t<F>, std::decay_t<BoundArgs>...>; } // namespace functional_internal ABSL_NAMESPACE_END
diff --git a/absl/functional/internal/function_ref.h b/absl/functional/internal/function_ref.h index 543b30f..46147e9 100644 --- a/absl/functional/internal/function_ref.h +++ b/absl/functional/internal/function_ref.h
@@ -47,8 +47,8 @@ template <typename T> struct PassByValue<T, /*IsLValueReference=*/false> : std::integral_constant<bool, - absl::is_trivially_copy_constructible<T>::value && - absl::is_trivially_copy_assignable< + std::is_trivially_copy_constructible<T>::value && + std::is_trivially_copy_assignable< typename std::remove_cv<T>::type>::value && std::is_trivially_destructible<T>::value && sizeof(T) <= 2 * sizeof(void*)> {};
diff --git a/absl/hash/hash.h b/absl/hash/hash.h index 23f4e9d..e2bde0c 100644 --- a/absl/hash/hash.h +++ b/absl/hash/hash.h
@@ -329,7 +329,7 @@ // users should not define their own HashState types. template < typename T, - absl::enable_if_t< + std::enable_if_t< std::is_base_of<hash_internal::HashStateBase<T>, T>::value, int> = 0> static HashState Create(T* state) { HashState s;
diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index 039515a..e83fcae 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc
@@ -803,8 +803,8 @@ EXPECT_TRUE(std::is_default_constructible<absl::Hash<int>>::value); EXPECT_TRUE(std::is_copy_constructible<absl::Hash<int>>::value); EXPECT_TRUE(std::is_move_constructible<absl::Hash<int>>::value); - EXPECT_TRUE(absl::is_copy_assignable<absl::Hash<int>>::value); - EXPECT_TRUE(absl::is_move_assignable<absl::Hash<int>>::value); + EXPECT_TRUE(std::is_copy_assignable<absl::Hash<int>>::value); + EXPECT_TRUE(std::is_move_assignable<absl::Hash<int>>::value); EXPECT_TRUE(IsHashCallable<int>::value); EXPECT_TRUE(IsAggregateInitializable<absl::Hash<int>>::value); } @@ -815,8 +815,8 @@ EXPECT_FALSE(std::is_default_constructible<absl::Hash<X>>::value); EXPECT_FALSE(std::is_copy_constructible<absl::Hash<X>>::value); EXPECT_FALSE(std::is_move_constructible<absl::Hash<X>>::value); - EXPECT_FALSE(absl::is_copy_assignable<absl::Hash<X>>::value); - EXPECT_FALSE(absl::is_move_assignable<absl::Hash<X>>::value); + EXPECT_FALSE(std::is_copy_assignable<absl::Hash<X>>::value); + EXPECT_FALSE(std::is_move_assignable<absl::Hash<X>>::value); EXPECT_FALSE(IsHashCallable<X>::value); #if !defined(__GNUC__) || defined(__clang__) // TODO(b/144368551): As of GCC 8.4 this does not compile. @@ -891,7 +891,7 @@ template <InvokeTag allowed, InvokeTag... tags> struct EnableIfContained - : std::enable_if<absl::disjunction< + : std::enable_if<std::disjunction< std::integral_constant<bool, allowed == tags>...>::value> {}; template <
diff --git a/absl/hash/hash_testing.h b/absl/hash/hash_testing.h index 509df07..3740ba9 100644 --- a/absl/hash/hash_testing.h +++ b/absl/hash/hash_testing.h
@@ -281,7 +281,7 @@ template <typename... T> struct TypeSet { - template <typename U, bool = disjunction<std::is_same<T, U>...>::value> + template <typename U, bool = std::disjunction<std::is_same<T, U>...>::value> struct Insert { using type = TypeSet<U, T...>; };
diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index a6c8313..ae3d9a2 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h
@@ -404,7 +404,7 @@ // Convenience function that combines `hash_state` with the byte representation // of `value`. template <typename H, typename T, - absl::enable_if_t<FitsIn64Bits<T>::value, int> = 0> + std::enable_if_t<FitsIn64Bits<T>::value, int> = 0> H hash_bytes(H hash_state, const T& value) { const unsigned char* start = reinterpret_cast<const unsigned char*>(&value); uint64_t v; @@ -421,7 +421,7 @@ return CombineRaw()(std::move(hash_state), v); } template <typename H, typename T, - absl::enable_if_t<!FitsIn64Bits<T>::value, int> = 0> + std::enable_if_t<!FitsIn64Bits<T>::value, int> = 0> H hash_bytes(H hash_state, const T& value) { const unsigned char* start = reinterpret_cast<const unsigned char*>(&value); return H::combine_contiguous(std::move(hash_state), start, sizeof(value)); @@ -601,7 +601,7 @@ // for now. H #else // _MSC_VER -typename std::enable_if<absl::conjunction<is_hashable<Ts>...>::value, H>::type +typename std::enable_if<std::conjunction<is_hashable<Ts>...>::value, H>::type #endif // _MSC_VER AbslHashValue(H hash_state, const std::tuple<Ts...>& t) { return hash_internal::hash_tuple(std::move(hash_state), t, @@ -650,7 +650,7 @@ // Support std::wstring, std::u16string and std::u32string. template <typename Char, typename Alloc, typename H, - typename = absl::enable_if_t<std::is_same<Char, wchar_t>::value || + typename = std::enable_if_t<std::is_same<Char, wchar_t>::value || std::is_same<Char, char16_t>::value || std::is_same<Char, char32_t>::value>> H AbslHashValue( @@ -661,7 +661,7 @@ // Support std::wstring_view, std::u16string_view and std::u32string_view. template <typename Char, typename H, - typename = absl::enable_if_t<std::is_same<Char, wchar_t>::value || + typename = std::enable_if_t<std::is_same<Char, wchar_t>::value || std::is_same<Char, char16_t>::value || std::is_same<Char, char32_t>::value>> H AbslHashValue(H hash_state, std::basic_string_view<Char> str) { @@ -680,7 +680,7 @@ // Support std::filesystem::path. The SFINAE is required because some string // types are implicitly convertible to std::filesystem::path. template <typename Path, typename H, - typename = absl::enable_if_t< + typename = std::enable_if_t< std::is_same_v<Path, std::filesystem::path>>> H AbslHashValue(H hash_state, const Path& path) { // This is implemented by deferring to the standard library to compute the @@ -919,7 +919,7 @@ // AbslHashValue for hashing std::variant template <typename H, typename... T> -typename std::enable_if<conjunction<is_hashable<T>...>::value, H>::type +typename std::enable_if<std::conjunction<is_hashable<T>...>::value, H>::type AbslHashValue(H hash_state, const std::variant<T...>& v) { if (!v.valueless_by_exception()) { hash_state = std::visit(VariantVisitor<H>{std::move(hash_state)}, v); @@ -1299,14 +1299,14 @@ struct UniquelyRepresentedProbe { template <typename H, typename T> static auto Invoke(H state, const T& value) - -> absl::enable_if_t<is_uniquely_represented<T>::value, H> { + -> std::enable_if_t<is_uniquely_represented<T>::value, H> { return hash_internal::hash_bytes(std::move(state), value); } }; struct HashValueProbe { template <typename H, typename T> - static auto Invoke(H state, const T& value) -> absl::enable_if_t< + static auto Invoke(H state, const T& value) -> std::enable_if_t< std::is_same<H, decltype(AbslHashValue(std::move(state), value))>::value, H> { @@ -1317,7 +1317,7 @@ struct LegacyHashProbe { #if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ template <typename H, typename T> - static auto Invoke(H state, const T& value) -> absl::enable_if_t< + static auto Invoke(H state, const T& value) -> std::enable_if_t< std::is_convertible< decltype(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE::hash<T>()(value)), size_t>::value, @@ -1332,7 +1332,7 @@ struct StdHashProbe { template <typename H, typename T> static auto Invoke(H state, const T& value) - -> absl::enable_if_t<type_traits_internal::IsHashable<T>::value, H> { + -> std::enable_if_t<type_traits_internal::IsHashable<T>::value, H> { return hash_internal::hash_bytes(std::move(state), std::hash<T>{}(value)); } }; @@ -1354,7 +1354,7 @@ // Probe each implementation in order. // disjunction provides short circuiting wrt instantiation. template <typename T> - using Apply = absl::disjunction< // + using Apply = std::disjunction< // Probe<WeaklyMixedIntegerProbe, T>, // Probe<UniquelyRepresentedProbe, T>, // Probe<HashValueProbe, T>, // @@ -1370,8 +1370,8 @@ class ABSL_DLL MixingHashState : public HashStateBase<MixingHashState> { template <typename T> using IntegralFastPath = - conjunction<std::is_integral<T>, is_uniquely_represented<T>, - FitsIn64Bits<T>>; + std::conjunction<std::is_integral<T>, is_uniquely_represented<T>, + FitsIn64Bits<T>>; public: // Move only @@ -1399,13 +1399,13 @@ // Otherwise we would be instantiating and calling dozens of functions for // something that is just one multiplication and a couple xor's. // The result should be the same as running the whole algorithm, but faster. - template <typename T, absl::enable_if_t<IntegralFastPath<T>::value, int> = 0> + template <typename T, std::enable_if_t<IntegralFastPath<T>::value, int> = 0> static size_t hash_with_seed(T value, size_t seed) { return static_cast<size_t>( CombineRawImpl(seed, static_cast<std::make_unsigned_t<T>>(value))); } - template <typename T, absl::enable_if_t<!IntegralFastPath<T>::value, int> = 0> + template <typename T, std::enable_if_t<!IntegralFastPath<T>::value, int> = 0> static size_t hash_with_seed(const T& value, size_t seed) { return static_cast<size_t>(combine(MixingHashState{seed}, value).state_); } @@ -1527,7 +1527,7 @@ template <typename T> struct Hash - : absl::conditional_t<is_hashable<T>::value, HashImpl<T>, PoisonedHash> {}; + : std::conditional_t<is_hashable<T>::value, HashImpl<T>, PoisonedHash> {}; template <typename H> template <typename T, typename... Ts>
diff --git a/absl/hash/internal/spy_hash_state.h b/absl/hash/internal/spy_hash_state.h index 80c9767..0eb9ffe 100644 --- a/absl/hash/internal/spy_hash_state.h +++ b/absl/hash/internal/spy_hash_state.h
@@ -22,6 +22,7 @@ #include <optional> #include <ostream> #include <string> +#include <type_traits> #include <vector> #include "absl/hash/hash.h" @@ -269,7 +270,7 @@ template < typename T, typename U, // Only trigger for when (T != U), - typename = absl::enable_if_t<!std::is_same<T, U>::value>, + typename = std::enable_if_t<!std::is_same<T, U>::value>, // This statement works in two ways: // - First, it instantiates RunOnStartup and forces the initialization of // `run`, which set the global variable.
diff --git a/absl/log/BUILD.bazel b/absl/log/BUILD.bazel index ec2fde9..e6793b1 100644 --- a/absl/log/BUILD.bazel +++ b/absl/log/BUILD.bazel
@@ -209,6 +209,7 @@ "//absl/strings", "//absl/strings:internal", "//absl/types:optional", + "//absl/types:source_location", "//absl/utility", ], ) @@ -404,6 +405,7 @@ "//absl/log/internal:test_helpers", "//absl/log/internal:test_matchers", "//absl/strings", + "//absl/types:source_location", "@googletest//:gtest", "@googletest//:gtest_main", ], @@ -564,6 +566,7 @@ "//absl/log/internal:test_helpers", "//absl/log/internal:test_matchers", "//absl/strings", + "//absl/types:source_location", "@googletest//:gtest", "@googletest//:gtest_main", ], @@ -584,6 +587,7 @@ "//absl/log/internal:test_matchers", "//absl/strings", "//absl/time", + "//absl/types:source_location", "@googletest//:gtest", "@googletest//:gtest_main", ],
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt index 609f432..b271d09 100644 --- a/absl/log/CMakeLists.txt +++ b/absl/log/CMakeLists.txt
@@ -215,6 +215,7 @@ absl::memory absl::nullability absl::raw_logging_internal + absl::source_location absl::span absl::strerror absl::strings @@ -632,6 +633,7 @@ absl::absl_log absl::log_severity absl::optional + absl::source_location absl::strings absl::strings_internal absl::utility @@ -1079,6 +1081,7 @@ absl::log_streamer absl::log_severity absl::scoped_mock_log + absl::source_location absl::strings GTest::gmock_main ) @@ -1099,6 +1102,7 @@ absl::log_internal_test_matchers absl::log_sink absl::scoped_mock_log + absl::source_location absl::strings absl::time GTest::gmock_main
diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel index 6635118..ebacbc1 100644 --- a/absl/log/internal/BUILD.bazel +++ b/absl/log/internal/BUILD.bazel
@@ -216,6 +216,7 @@ "//absl/strings", "//absl/strings:internal", "//absl/time", + "//absl/types:source_location", "//absl/types:span", ], )
diff --git a/absl/log/internal/log_message.h b/absl/log/internal/log_message.h index 5b6eed3..7e2a86a 100644 --- a/absl/log/internal/log_message.h +++ b/absl/log/internal/log_message.h
@@ -50,6 +50,8 @@ #include "absl/strings/has_absl_stringify.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" +#include "absl/types/source_location.h" +#include "absl/types/span.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -86,6 +88,11 @@ // Overrides the location inferred from the callsite. The string pointed to // by `file` must be valid until the end of the statement. LogMessage& AtLocation(absl::string_view file, int line); + // `loc` doesn't default to `absl::SourceLocation::current()` here since the + // callsite is already the default location for `LOG` statements. + LogMessage& AtLocation(absl::SourceLocation loc) { + return AtLocation(loc.file_name(), static_cast<int>(loc.line())); + } // Omits the prefix from this line. The prefix includes metadata about the // logged data such as source code location and timestamp. LogMessage& NoPrefix();
diff --git a/absl/log/internal/nullstream.h b/absl/log/internal/nullstream.h index c87f9aa..4eae52e 100644 --- a/absl/log/internal/nullstream.h +++ b/absl/log/internal/nullstream.h
@@ -125,4 +125,4 @@ ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_LOG_INTERNAL_GLOBALS_H_ +#endif // ABSL_LOG_INTERNAL_NULLSTREAM_H_
diff --git a/absl/log/log_modifier_methods_test.cc b/absl/log/log_modifier_methods_test.cc index 7893557..1c7c35d 100644 --- a/absl/log/log_modifier_methods_test.cc +++ b/absl/log/log_modifier_methods_test.cc
@@ -28,6 +28,7 @@ #include "absl/strings/match.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" +#include "absl/types/source_location.h" namespace { #if GTEST_HAS_DEATH_TEST @@ -88,6 +89,23 @@ << "hello world"; } +TEST(TailCallsModifiesTest, AtLocationSourceLocation) { + absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); + EXPECT_CALL(test_sink, Send).Times(0); + + const int log_line = __LINE__ + 1; + constexpr absl::SourceLocation loc = absl::SourceLocation::current(); + auto do_log = [loc] { LOG(INFO).AtLocation(loc) << "hello world"; }; + + EXPECT_CALL(test_sink, + Send(AllOf(SourceFilename(Eq(__FILE__)), + SourceBasename(Eq("log_modifier_methods_test.cc")), + SourceLine(Eq(log_line))))); + + test_sink.StartCapturingLogs(); + do_log(); +} + TEST(TailCallsModifiesTest, NoPrefix) { absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); EXPECT_CALL(test_sink, Send).Times(0);
diff --git a/absl/log/log_streamer.h b/absl/log/log_streamer.h index 32c3402..213e352 100644 --- a/absl/log/log_streamer.h +++ b/absl/log/log_streamer.h
@@ -35,6 +35,7 @@ #include "absl/strings/internal/ostringstream.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "absl/types/source_location.h" #include "absl/utility/utility.h" namespace absl { @@ -87,6 +88,10 @@ // To match `LOG`'s defaults: stream_->setf(std::ios_base::showbase | std::ios_base::boolalpha); } + explicit LogStreamer( + absl::LogSeverity severity, + absl::SourceLocation loc = absl::SourceLocation::current()) + : LogStreamer(severity, loc.file_name(), static_cast<int>(loc.line())) {} // A moved-from `absl::LogStreamer` does not `LOG` when destroyed, // and a program that streams into one has undefined behavior. @@ -176,6 +181,27 @@ return absl::LogStreamer(absl::kLogDebugFatal, file, line); } +inline LogStreamer LogInfoStreamer( + absl::SourceLocation loc = absl::SourceLocation::current()) { + return absl::LogStreamer(absl::LogSeverity::kInfo, loc); +} +inline LogStreamer LogWarningStreamer( + absl::SourceLocation loc = absl::SourceLocation::current()) { + return absl::LogStreamer(absl::LogSeverity::kWarning, loc); +} +inline LogStreamer LogErrorStreamer( + absl::SourceLocation loc = absl::SourceLocation::current()) { + return absl::LogStreamer(absl::LogSeverity::kError, loc); +} +inline LogStreamer LogFatalStreamer( + absl::SourceLocation loc = absl::SourceLocation::current()) { + return absl::LogStreamer(absl::LogSeverity::kFatal, loc); +} +inline LogStreamer LogDebugFatalStreamer( + absl::SourceLocation loc = absl::SourceLocation::current()) { + return absl::LogStreamer(absl::kLogDebugFatal, loc); +} + ABSL_NAMESPACE_END } // namespace absl
diff --git a/absl/log/log_streamer_test.cc b/absl/log/log_streamer_test.cc index f226fef..e45a293 100644 --- a/absl/log/log_streamer_test.cc +++ b/absl/log/log_streamer_test.cc
@@ -30,6 +30,7 @@ #include "absl/log/log.h" #include "absl/log/scoped_mock_log.h" #include "absl/strings/string_view.h" +#include "absl/types/source_location.h" namespace { using ::absl::log_internal::DeathTestExpectedLogging; @@ -448,4 +449,21 @@ LOG(INFO) << false << std::hex << 0xdeadbeef; } +TEST(LogStreamerTest, AtSourceLocation) { + const int log_line = __LINE__ + 2; + auto do_log = [] { + WriteToStream("foo", &absl::LogInfoStreamer().stream()); // + }; + absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); + EXPECT_CALL(test_sink, Send).Times(0); + + EXPECT_CALL(test_sink, + Send(AllOf(SourceFilename( + Eq(absl::SourceLocation::current().file_name())), + SourceLine(Eq(log_line))))); + + test_sink.StartCapturingLogs(); + do_log(); +} + } // namespace
diff --git a/absl/memory/memory.h b/absl/memory/memory.h index f0f7aea..bc5d654 100644 --- a/absl/memory/memory.h +++ b/absl/memory/memory.h
@@ -134,7 +134,7 @@ template <typename T> typename memory_internal::MakeUniqueResult<T>::array make_unique_for_overwrite(size_t n) { - return std::unique_ptr<T>(new typename absl::remove_extent_t<T>[n]); + return std::unique_ptr<T>(new typename std::remove_extent_t<T>[n]); } // `absl::make_unique_for_overwrite` overload for an array T[N] of known bounds.
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index 2c651f2..9013aea 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h
@@ -45,6 +45,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" +#include "absl/base/macros.h" #ifdef __cpp_lib_span #include <span> // NOLINT(build/c++20) @@ -115,38 +116,114 @@ // // See the documentation for the STL <type_traits> header for more information: // https://en.cppreference.com/w/cpp/header/type_traits -using std::add_const_t; -using std::add_cv_t; -using std::add_lvalue_reference_t; -using std::add_pointer_t; -using std::add_rvalue_reference_t; -using std::add_volatile_t; -using std::common_type_t; -using std::conditional_t; -using std::conjunction; -using std::decay_t; -using std::enable_if_t; -using std::disjunction; -using std::is_copy_assignable; -using std::is_function; -using std::is_move_assignable; -using std::is_trivially_copy_assignable; -using std::is_trivially_copy_constructible; -using std::is_trivially_default_constructible; -using std::is_trivially_destructible; -using std::is_trivially_move_assignable; -using std::is_trivially_move_constructible; -using std::make_signed_t; -using std::make_unsigned_t; -using std::negation; -using std::remove_all_extents_t; -using std::remove_const_t; -using std::remove_cv_t; -using std::remove_extent_t; -using std::remove_pointer_t; -using std::remove_reference_t; -using std::remove_volatile_t; -using std::underlying_type_t; + +template <class T> +using add_const_t ABSL_DEPRECATE_AND_INLINE() = std::add_const_t<T>; + +template <class T> +using add_cv_t ABSL_DEPRECATE_AND_INLINE() = std::add_cv_t<T>; + +template <class T> +using add_lvalue_reference_t ABSL_DEPRECATE_AND_INLINE() = + std::add_lvalue_reference_t<T>; + +template <class T> +using add_pointer_t ABSL_DEPRECATE_AND_INLINE() = std::add_pointer_t<T>; + +template <class T> +using add_rvalue_reference_t ABSL_DEPRECATE_AND_INLINE() = + std::add_rvalue_reference_t<T>; + +template <class T> +using add_volatile_t ABSL_DEPRECATE_AND_INLINE() = std::add_volatile_t<T>; + +template <class... T> +using common_type_t ABSL_DEPRECATE_AND_INLINE() = std::common_type_t<T...>; + +template <bool C, class T, class F> +using conditional_t ABSL_DEPRECATE_AND_INLINE() = std::conditional_t<C, T, F>; + +template <class... T> +using conjunction ABSL_DEPRECATE_AND_INLINE() = std::conjunction<T...>; + +template <class T> +using decay_t ABSL_DEPRECATE_AND_INLINE() = std::decay_t<T>; + +template <bool C, class T = void> +using enable_if_t ABSL_DEPRECATE_AND_INLINE() = std::enable_if_t<C, T>; + +template <class... T> +using disjunction ABSL_DEPRECATE_AND_INLINE() = std::disjunction<T...>; + +template <class T> +using is_copy_assignable ABSL_DEPRECATE_AND_INLINE() = + std::is_copy_assignable<T>; + +template <class T> +using is_function ABSL_DEPRECATE_AND_INLINE() = std::is_function<T>; + +template <class T> +using is_move_assignable ABSL_DEPRECATE_AND_INLINE() = + std::is_move_assignable<T>; + +template <class T> +using is_trivially_copy_assignable ABSL_DEPRECATE_AND_INLINE() = + std::is_trivially_copy_assignable<T>; + +template <class T> +using is_trivially_copy_constructible ABSL_DEPRECATE_AND_INLINE() = + std::is_trivially_copy_constructible<T>; + +template <class T> +using is_trivially_default_constructible ABSL_DEPRECATE_AND_INLINE() = + std::is_trivially_default_constructible<T>; + +template <class T> +using is_trivially_destructible ABSL_DEPRECATE_AND_INLINE() = + std::is_trivially_destructible<T>; + +template <class T> +using is_trivially_move_assignable ABSL_DEPRECATE_AND_INLINE() = + std::is_trivially_move_assignable<T>; + +template <class T> +using is_trivially_move_constructible ABSL_DEPRECATE_AND_INLINE() = + std::is_trivially_move_constructible<T>; + +template <class T> +using make_signed_t ABSL_DEPRECATE_AND_INLINE() = std::make_signed_t<T>; + +template <class T> +using make_unsigned_t ABSL_DEPRECATE_AND_INLINE() = std::make_unsigned_t<T>; + +template <class T> +using negation ABSL_DEPRECATE_AND_INLINE() = std::negation<T>; + +template <class T> +using remove_all_extents_t ABSL_DEPRECATE_AND_INLINE() = + std::remove_all_extents_t<T>; + +template <class T> +using remove_const_t ABSL_DEPRECATE_AND_INLINE() = std::remove_const_t<T>; + +template <class T> +using remove_cv_t ABSL_DEPRECATE_AND_INLINE() = std::remove_cv_t<T>; + +template <class T> +using remove_extent_t ABSL_DEPRECATE_AND_INLINE() = std::remove_extent_t<T>; + +template <class T> +using remove_pointer_t ABSL_DEPRECATE_AND_INLINE() = std::remove_pointer_t<T>; + +template <class T> +using remove_reference_t ABSL_DEPRECATE_AND_INLINE() = + std::remove_reference_t<T>; + +template <class T> +using remove_volatile_t ABSL_DEPRECATE_AND_INLINE() = std::remove_volatile_t<T>; + +template <class T> +using underlying_type_t ABSL_DEPRECATE_AND_INLINE() = std::underlying_type_t<T>; #if defined(__cpp_lib_remove_cvref) && __cpp_lib_remove_cvref >= 201711L template <typename T> @@ -218,7 +295,7 @@ template <typename Key> struct IsHashable< Key, - absl::enable_if_t<std::is_convertible< + std::enable_if_t<std::is_convertible< decltype(std::declval<std::hash<Key>&>()(std::declval<Key const&>())), std::size_t>::value>> : std::true_type {}; @@ -243,7 +320,7 @@ static_assert( std::is_copy_constructible<std::hash<Key>>::value, "std::hash<Key> must be copy constructible when it is enabled"); - static_assert(absl::is_copy_assignable<std::hash<Key>>::value, + static_assert(std::is_copy_assignable<std::hash<Key>>::value, "std::hash<Key> must be copy assignable when it is enabled"); // is_destructible is unchecked as it's implied by each of the // is_constructible checks. @@ -306,7 +383,7 @@ // // Performs the swap idiom from a namespace where valid candidates may only be // found in `std` or via ADL. -template <class T, absl::enable_if_t<IsSwappable<T>::value, int> = 0> +template <class T, std::enable_if_t<IsSwappable<T>::value, int> = 0> void Swap(T& lhs, T& rhs) noexcept(IsNothrowSwappable<T>::value) { swap(lhs, rhs); } @@ -475,7 +552,7 @@ struct IsOwnerImpl< T, std::enable_if_t<std::is_class<typename T::absl_internal_is_view>::value>> - : absl::negation<typename T::absl_internal_is_view> {}; + : std::negation<typename T::absl_internal_is_view> {}; // A trait to determine whether a type is an owner. // Do *not* depend on the correctness of this trait for correct code behavior. @@ -555,7 +632,7 @@ // Until then, we consider an assignment from an "owner" (such as std::string) // to a "view" (such as std::string_view) to be a lifetime-bound assignment. template <typename T, typename U> -using IsLifetimeBoundAssignment = absl::conjunction< +using IsLifetimeBoundAssignment = std::conjunction< std::integral_constant<bool, !std::is_lvalue_reference<U>::value>, IsOwner<absl::remove_cvref_t<U>>, IsView<absl::remove_cvref_t<T>>>;
diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc index 9a8262d..6d0085c 100644 --- a/absl/meta/type_traits_test.cc +++ b/absl/meta/type_traits_test.cc
@@ -33,8 +33,8 @@ template <typename T> using IsOwnerAndNotView = - absl::conjunction<absl::type_traits_internal::IsOwner<T>, - absl::negation<absl::type_traits_internal::IsView<T>>>; + std::conjunction<absl::type_traits_internal::IsOwner<T>, + std::negation<absl::type_traits_internal::IsView<T>>>; static_assert( IsOwnerAndNotView<std::pair<std::vector<int>, std::string>>::value, @@ -70,13 +70,13 @@ struct TypeWithBarFunction { template <class T, - absl::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0> + std::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0> ReturnType bar(T&&, const StructB&, StructC&&) &&; // NOLINT }; struct TypeWithBarFunctionAndConvertibleReturnType { template <class T, - absl::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0> + std::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0> ConvertibleToReturnType bar(T&&, const StructB&, StructC&&) &&; // NOLINT }; @@ -160,19 +160,19 @@ struct GetTypeT { template <typename T, - absl::enable_if_t<std::is_same<T, TypeA>::value, int> = 0> + std::enable_if_t<std::is_same<T, TypeA>::value, int> = 0> TypeEnum operator()(Wrap<T>) const { return TypeEnum::A; } template <typename T, - absl::enable_if_t<std::is_same<T, TypeB>::value, int> = 0> + std::enable_if_t<std::is_same<T, TypeB>::value, int> = 0> TypeEnum operator()(Wrap<T>) const { return TypeEnum::B; } template <typename T, - absl::enable_if_t<std::is_same<T, TypeC>::value, int> = 0> + std::enable_if_t<std::is_same<T, TypeC>::value, int> = 0> TypeEnum operator()(Wrap<T>) const { return TypeEnum::C; }
diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc index 77ee63c..336db78 100644 --- a/absl/numeric/int128_test.cc +++ b/absl/numeric/int128_test.cc
@@ -93,11 +93,11 @@ #endif // ABSL_HAVE_INTRINSIC_INT128 TEST(Uint128, TrivialTraitsTest) { - static_assert(absl::is_trivially_default_constructible<absl::uint128>::value, + static_assert(std::is_trivially_default_constructible<absl::uint128>::value, ""); - static_assert(absl::is_trivially_copy_constructible<absl::uint128>::value, + static_assert(std::is_trivially_copy_constructible<absl::uint128>::value, ""); - static_assert(absl::is_trivially_copy_assignable<absl::uint128>::value, ""); + static_assert(std::is_trivially_copy_assignable<absl::uint128>::value, ""); static_assert(std::is_trivially_destructible<absl::uint128>::value, ""); } @@ -619,10 +619,10 @@ #endif // ABSL_HAVE_INTRINSIC_INT128 TEST(Int128, TrivialTraitsTest) { - static_assert(absl::is_trivially_default_constructible<absl::int128>::value, + static_assert(std::is_trivially_default_constructible<absl::int128>::value, ""); - static_assert(absl::is_trivially_copy_constructible<absl::int128>::value, ""); - static_assert(absl::is_trivially_copy_assignable<absl::int128>::value, ""); + static_assert(std::is_trivially_copy_constructible<absl::int128>::value, ""); + static_assert(std::is_trivially_copy_assignable<absl::int128>::value, ""); static_assert(std::is_trivially_destructible<absl::int128>::value, ""); }
diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index 8986211..28f6872 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel
@@ -128,13 +128,14 @@ copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":mocking_access", ":random", "//absl/base:config", "//absl/base:core_headers", "//absl/base:fast_type_id", "//absl/meta:type_traits", - "//absl/random/internal:distribution_caller", "//absl/random/internal:fast_uniform_bits", + "//absl/random/internal:traits", ], ) @@ -160,6 +161,7 @@ ], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":mocking_access", ":random", "//absl/base:config", "//absl/base:fast_type_id", @@ -171,6 +173,16 @@ ], ) +cc_library( + name = "mocking_access", + hdrs = ["mocking_access.h"], + deps = [ + "//absl/base:config", + "//absl/base:fast_type_id", + "//absl/meta:type_traits", + ], +) + cc_test( name = "bernoulli_distribution_test", size = "small",
diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 7382b0d..4a13e84 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt
@@ -44,9 +44,10 @@ DEPS absl::config absl::core_headers - absl::random_internal_distribution_caller absl::random_internal_fast_uniform_bits absl::type_traits + absl::random_mocking_access + absl::random_internal_traits ) absl_cc_test( @@ -68,6 +69,21 @@ GTest::gtest_main ) +absl_cc_library( + NAME + random_mocking_access + HDRS + "mocking_access.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config + absl::fast_type_id + absl::type_traits +) + # Internal-only target, do not depend on directly. absl_cc_library( NAME @@ -82,6 +98,7 @@ absl::config absl::fast_type_id absl::optional + absl::random_mocking_access ) # Internal-only target, do not depend on directly. @@ -118,6 +135,7 @@ absl::flat_hash_map absl::raw_logging_internal absl::random_internal_mock_helpers + absl::random_mocking_access absl::random_random absl::type_traits absl::utility @@ -536,6 +554,9 @@ ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config + absl::type_traits + absl::bits + absl::int128 ) # Internal-only target, do not depend on directly. @@ -553,6 +574,7 @@ absl::utility absl::fast_type_id absl::type_traits + absl::random_mocking_access ) # Internal-only target, do not depend on directly.
diff --git a/absl/random/benchmarks.cc b/absl/random/benchmarks.cc index 958dfe4..4cb16e6 100644 --- a/absl/random/benchmarks.cc +++ b/absl/random/benchmarks.cc
@@ -97,7 +97,7 @@ template <typename Engine, typename SSeq = DefaultConstructorSeedSeq> Engine make_engine() { constexpr bool use_default_initialization = - std::is_same_v<SSeq, DefaultConstructorSeedSeq>; + std::is_same_v<SSeq, DefaultConstructorSeedSeq>; if constexpr (use_default_initialization) { return Engine(); } else {
diff --git a/absl/random/beta_distribution.h b/absl/random/beta_distribution.h index a362345..bedeca6 100644 --- a/absl/random/beta_distribution.h +++ b/absl/random/beta_distribution.h
@@ -282,7 +282,7 @@ using random_internal::GeneratePositiveTag; using random_internal::GenerateRealFromBits; using real_type = - absl::conditional_t<std::is_same<RealType, float>::value, float, double>; + std::conditional_t<std::is_same<RealType, float>::value, float, double>; // Based on Joehnk, M. D. Erzeugung von betaverteilten und gammaverteilten // Zufallszahlen. Metrika 8.1 (1964): 5-15. @@ -340,7 +340,7 @@ using random_internal::GeneratePositiveTag; using random_internal::GenerateRealFromBits; using real_type = - absl::conditional_t<std::is_same<RealType, float>::value, float, double>; + std::conditional_t<std::is_same<RealType, float>::value, float, double>; // Based on Cheng, Russell CH. Generating beta variates with nonintegral // shape parameters. Communications of the ACM 21.4 (1978): 317-322.
diff --git a/absl/random/bit_gen_ref.h b/absl/random/bit_gen_ref.h index dfce2c4..262eb9a 100644 --- a/absl/random/bit_gen_ref.h +++ b/absl/random/bit_gen_ref.h
@@ -27,41 +27,17 @@ #include <cstdint> #include <limits> #include <type_traits> -#include <utility> #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/fast_type_id.h" #include "absl/meta/type_traits.h" -#include "absl/random/internal/distribution_caller.h" #include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/traits.h" +#include "absl/random/mocking_access.h" namespace absl { ABSL_NAMESPACE_BEGIN -namespace random_internal { - -template <typename URBG, typename = void, typename = void, typename = void> -struct is_urbg : std::false_type {}; - -template <typename URBG> -struct is_urbg< - URBG, - absl::enable_if_t<std::is_same< - typename URBG::result_type, - typename std::decay<decltype((URBG::min)())>::type>::value>, - absl::enable_if_t<std::is_same< - typename URBG::result_type, - typename std::decay<decltype((URBG::max)())>::type>::value>, - absl::enable_if_t<std::is_same< - typename URBG::result_type, - typename std::decay<decltype(std::declval<URBG>()())>::type>::value>> - : std::true_type {}; - -template <typename> -struct DistributionCaller; -class MockHelpers; - -} // namespace random_internal // ----------------------------------------------------------------------------- // absl::BitGenRef @@ -87,24 +63,18 @@ // } // class BitGenRef { - // SFINAE to detect whether the URBG type includes a member matching - // bool InvokeMock(key_id, args_tuple*, result*). - // - // These live inside BitGenRef so that they have friend access - // to MockingBitGen. (see similar methods in DistributionCaller). template <template <class...> class Trait, class AlwaysVoid, class... Args> struct detector : std::false_type {}; template <template <class...> class Trait, class... Args> struct detector<Trait, absl::void_t<Trait<Args...>>, Args...> : std::true_type {}; - template <class T> - using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock( - std::declval<FastTypeIdType>(), std::declval<void*>(), - std::declval<void*>())); + template <typename T> + using has_conversion_operator_t = + decltype(std::declval<T>().operator BitGenRef()); template <typename T> - using HasInvokeMock = typename detector<invoke_mock_t, void, T>::type; + using HasConversionOperator = detector<has_conversion_operator_t, void, T>; public: BitGenRef(const BitGenRef&) = default; @@ -112,23 +82,28 @@ BitGenRef& operator=(const BitGenRef&) = default; BitGenRef& operator=(BitGenRef&&) = default; - template < - typename URBGRef, typename URBG = absl::remove_cvref_t<URBGRef>, - typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value && - random_internal::is_urbg<URBG>::value && - !HasInvokeMock<URBG>::value)>* = nullptr> + template <typename URBGRef, typename URBG = absl::remove_cvref_t<URBGRef>, + typename std::enable_if_t< + (!std::is_same<URBG, BitGenRef>::value && + !std::is_base_of<BitGenRef, URBG>::value && + !HasConversionOperator<URBG>::value && + random_internal::is_urbg<URBG>::value && + !RandomMockingAccess::HasInvokeMock<URBG>::value)>* = nullptr> BitGenRef(URBGRef&& gen ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), mock_call_(NotAMock), generate_impl_fn_(ImplFn<URBG>) {} template <typename URBGRef, typename URBG = absl::remove_cvref_t<URBGRef>, - typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value && - random_internal::is_urbg<URBG>::value && - HasInvokeMock<URBG>::value)>* = nullptr> + typename std::enable_if_t< + (!std::is_same<URBG, BitGenRef>::value && + !std::is_base_of<BitGenRef, URBG>::value && + !HasConversionOperator<URBG>::value && + random_internal::is_urbg<URBG>::value && + RandomMockingAccess::HasInvokeMock<URBG>::value)>* = nullptr> BitGenRef(URBGRef&& gen ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), - mock_call_(&MockCall<URBG>), + mock_call_(MockCall<URBG>), generate_impl_fn_(ImplFn<URBG>) {} using result_type = uint64_t; @@ -157,10 +132,10 @@ // Get a type-erased InvokeMock pointer. template <typename URBG> - static bool MockCall(uintptr_t gen_ptr, FastTypeIdType key_id, void* result, - void* arg_tuple) { - return reinterpret_cast<URBG*>(gen_ptr)->InvokeMock(key_id, result, - arg_tuple); + static bool MockCall(uintptr_t gen_ptr, FastTypeIdType key_id, + void* args_tuple, void* result) { + return RandomMockingAccess::InvokeMock(reinterpret_cast<URBG*>(gen_ptr), + key_id, args_tuple, result); } static bool NotAMock(uintptr_t, FastTypeIdType, void*, void*) { return false; @@ -176,9 +151,7 @@ mock_call_fn mock_call_; impl_fn generate_impl_fn_; - template <typename> - friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock - friend class ::absl::random_internal::MockHelpers; // for InvokeMock + friend class ::absl::RandomMockingAccess; // for InvokeMock }; ABSL_NAMESPACE_END
diff --git a/absl/random/bit_gen_ref_test.cc b/absl/random/bit_gen_ref_test.cc index d581352..d04ac3a 100644 --- a/absl/random/bit_gen_ref_test.cc +++ b/absl/random/bit_gen_ref_test.cc
@@ -17,6 +17,7 @@ #include <cstdint> #include <random> +#include <type_traits> #include <vector> #include "gmock/gmock.h" @@ -102,6 +103,48 @@ absl::BitGenRef gen_ref(const_gen); EXPECT_EQ(FnTest(gen_ref), 42); // Copy } + +struct MinStdRand { + // The URBG just returns 0. + using result_type = absl::BitGen::result_type; + static constexpr result_type(min)() { return (absl::BitGen::min)(); } + static constexpr result_type(max)() { return (absl::BitGen::max)(); } + result_type operator()() { return 0; } + + // Implicit conversions allow passing MinStdRand to functions taking + // absl::BitGenRef as well as explicitly constructing an absl::BitGenRef from + // a MinStdRand. + operator absl::BitGenRef() const { + conversion_count++; + return absl::BitGenRef(minstd_gen); + } + + std::minstd_rand minstd_gen; + mutable int conversion_count = 0; +}; + +TEST(BitGenRefTest, IsConvertibleTest) { + // Verify that MinStdRandBitGen is convertible to absl::BitGenRef. + EXPECT_TRUE((std::is_convertible<MinStdRand, absl::BitGenRef>::value)); + + // Explicit construction should trigger the conversion. + { + MinStdRand minstd; + absl::BitGenRef gen_ref(minstd); + EXPECT_EQ(minstd.conversion_count, 1); + (void)gen_ref; + } + + // Calling a function that accepts absl::BitGenRef should trigger the + // conversion. This tests implicit conversion. + { + MinStdRand minstd; + auto result = FnTest(minstd); + EXPECT_EQ(minstd.conversion_count, 1); + EXPECT_GE(result, 1); + } +} + } // namespace ABSL_NAMESPACE_END } // namespace absl
diff --git a/absl/random/distributions.h b/absl/random/distributions.h index dced895..01dc978 100644 --- a/absl/random/distributions.h +++ b/absl/random/distributions.h
@@ -117,11 +117,11 @@ // auto x = absl::Uniform<float>(bitgen, 0, 1); // template <typename R = void, typename TagType, typename URBG> -typename absl::enable_if_t<!std::is_same<R, void>::value, R> // +typename std::enable_if_t<!std::is_same<R, void>::value, R> // Uniform(TagType tag, URBG&& urbg, // NOLINT(runtime/references) R lo, R hi) { - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using distribution_t = random_internal::UniformDistributionWrapper<R>; auto a = random_internal::uniform_lower_bound(tag, lo, hi); @@ -137,10 +137,10 @@ // Overload of `Uniform()` using the default closed-open interval of [lo, hi), // and returning values of type `T` template <typename R = void, typename URBG> -typename absl::enable_if_t<!std::is_same<R, void>::value, R> // +typename std::enable_if_t<!std::is_same<R, void>::value, R> // Uniform(URBG&& urbg, // NOLINT(runtime/references) R lo, R hi) { - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using distribution_t = random_internal::UniformDistributionWrapper<R>; constexpr auto tag = absl::IntervalClosedOpen; @@ -159,12 +159,12 @@ // correctly from the passed types. template <typename R = void, typename TagType, typename URBG, typename A, typename B> -typename absl::enable_if_t<std::is_same<R, void>::value, +typename std::enable_if_t<std::is_same<R, void>::value, random_internal::uniform_inferred_return_t<A, B>> Uniform(TagType tag, URBG&& urbg, // NOLINT(runtime/references) A lo, B hi) { - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using return_t = typename random_internal::uniform_inferred_return_t<A, B>; using distribution_t = random_internal::UniformDistributionWrapper<return_t>; @@ -183,11 +183,11 @@ // default closed-open interval of [lo, hi). Note that a compile-error will // result if the return type cannot be deduced correctly from the passed types. template <typename R = void, typename URBG, typename A, typename B> -typename absl::enable_if_t<std::is_same<R, void>::value, +typename std::enable_if_t<std::is_same<R, void>::value, random_internal::uniform_inferred_return_t<A, B>> Uniform(URBG&& urbg, // NOLINT(runtime/references) A lo, B hi) { - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using return_t = typename random_internal::uniform_inferred_return_t<A, B>; using distribution_t = random_internal::UniformDistributionWrapper<return_t>; @@ -206,9 +206,9 @@ // Overload of Uniform() using the minimum and maximum values of a given type // `T` (which must be unsigned), returning a value of type `unsigned T` template <typename R, typename URBG> -typename absl::enable_if_t<!std::numeric_limits<R>::is_signed, R> // +typename std::enable_if_t<!std::numeric_limits<R>::is_signed, R> // Uniform(URBG&& urbg) { // NOLINT(runtime/references) - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using distribution_t = random_internal::UniformDistributionWrapper<R>; return random_internal::DistributionCaller<gen_t>::template Call< @@ -238,7 +238,7 @@ template <typename URBG> bool Bernoulli(URBG&& urbg, // NOLINT(runtime/references) double p) { - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using distribution_t = absl::bernoulli_distribution; return random_internal::DistributionCaller<gen_t>::template Call< @@ -270,7 +270,7 @@ "Template-argument 'RealType' must be a floating-point type, in " "absl::Beta<RealType, URBG>(...)"); - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using distribution_t = typename absl::beta_distribution<RealType>; return random_internal::DistributionCaller<gen_t>::template Call< @@ -302,7 +302,7 @@ "Template-argument 'RealType' must be a floating-point type, in " "absl::Exponential<RealType, URBG>(...)"); - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using distribution_t = typename absl::exponential_distribution<RealType>; return random_internal::DistributionCaller<gen_t>::template Call< @@ -333,7 +333,7 @@ "Template-argument 'RealType' must be a floating-point type, in " "absl::Gaussian<RealType, URBG>(...)"); - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using distribution_t = typename absl::gaussian_distribution<RealType>; return random_internal::DistributionCaller<gen_t>::template Call< @@ -375,7 +375,7 @@ "Template-argument 'IntType' must be an integral type, in " "absl::LogUniform<IntType, URBG>(...)"); - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using distribution_t = typename absl::log_uniform_int_distribution<IntType>; return random_internal::DistributionCaller<gen_t>::template Call< @@ -405,7 +405,7 @@ "Template-argument 'IntType' must be an integral type, in " "absl::Poisson<IntType, URBG>(...)"); - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using distribution_t = typename absl::poisson_distribution<IntType>; return random_internal::DistributionCaller<gen_t>::template Call< @@ -437,7 +437,7 @@ "Template-argument 'IntType' must be an integral type, in " "absl::Zipf<IntType, URBG>(...)"); - using gen_t = absl::decay_t<URBG>; + using gen_t = std::decay_t<URBG>; using distribution_t = typename absl::zipf_distribution<IntType>; return random_internal::DistributionCaller<gen_t>::template Call<
diff --git a/absl/random/distributions_test.cc b/absl/random/distributions_test.cc index 4340aeb..1fa3345 100644 --- a/absl/random/distributions_test.cc +++ b/absl/random/distributions_test.cc
@@ -80,13 +80,13 @@ template <typename A, typename B, typename Expect> void CheckArgsInferType() { static_assert( - absl::conjunction< + std::conjunction< std::is_same<Expect, decltype(InferredUniformReturnT<A, B>(0))>, std::is_same<Expect, decltype(InferredUniformReturnT<B, A>(0))>>::value, ""); static_assert( - absl::conjunction< + std::conjunction< std::is_same<Expect, decltype(InferredTaggedUniformReturnT< absl::IntervalOpenOpenTag, A, B>(0))>, std::is_same<Expect, @@ -121,14 +121,14 @@ template <typename A, typename B, typename Expect> void CheckArgsReturnExpectedType() { static_assert( - absl::conjunction< + std::conjunction< std::is_same<Expect, decltype(ExplicitUniformReturnT<A, B, Expect>(0))>, std::is_same<Expect, decltype(ExplicitUniformReturnT<B, A, Expect>( 0))>>::value, ""); static_assert( - absl::conjunction< + std::conjunction< std::is_same<Expect, decltype(ExplicitTaggedUniformReturnT< absl::IntervalOpenOpenTag, A, B, Expect>(0))>,
diff --git a/absl/random/exponential_distribution.h b/absl/random/exponential_distribution.h index 4af2fb4..f0267b2 100644 --- a/absl/random/exponential_distribution.h +++ b/absl/random/exponential_distribution.h
@@ -124,7 +124,7 @@ using random_internal::GenerateNegativeTag; using random_internal::GenerateRealFromBits; using real_type = - absl::conditional_t<std::is_same<RealType, float>::value, float, double>; + std::conditional_t<std::is_same<RealType, float>::value, float, double>; const result_type u = GenerateRealFromBits<real_type, GenerateNegativeTag, false>(fast_u64_(g)); // U(-1, 0)
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index c2f2c36..56377ed 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel
@@ -116,6 +116,7 @@ linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", + "//absl/meta:type_traits", "//absl/numeric:bits", "//absl/numeric:int128", ], @@ -130,6 +131,7 @@ "//absl/base:config", "//absl/base:fast_type_id", "//absl/meta:type_traits", + "//absl/random:mocking_access", "//absl/utility", ], ) @@ -592,7 +594,7 @@ deps = [ "//absl/base:config", "//absl/base:fast_type_id", - "//absl/types:optional", + "//absl/random:mocking_access", ], )
diff --git a/absl/random/internal/distribution_caller.h b/absl/random/internal/distribution_caller.h index e84ec8c..d14fa85 100644 --- a/absl/random/internal/distribution_caller.h +++ b/absl/random/internal/distribution_caller.h
@@ -24,6 +24,7 @@ #include "absl/base/config.h" #include "absl/base/fast_type_id.h" #include "absl/meta/type_traits.h" +#include "absl/random/mocking_access.h" #include "absl/utility/utility.h" namespace absl { @@ -37,23 +38,8 @@ struct DistributionCaller { static_assert(!std::is_pointer<URBG>::value, "You must pass a reference, not a pointer."); - // SFINAE to detect whether the URBG type includes a member matching - // bool InvokeMock(key_id, args_tuple*, result*). - // - // These live inside BitGenRef so that they have friend access - // to MockingBitGen. (see similar methods in DistributionCaller). - template <template <class...> class Trait, class AlwaysVoid, class... Args> - struct detector : std::false_type {}; - template <template <class...> class Trait, class... Args> - struct detector<Trait, absl::void_t<Trait<Args...>>, Args...> - : std::true_type {}; - template <class T> - using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock( - std::declval<FastTypeIdType>(), std::declval<void*>(), - std::declval<void*>())); - - using HasInvokeMock = typename detector<invoke_mock_t, void, URBG>::type; + using RandomMockingAccess = ::absl::RandomMockingAccess; // Default implementation of distribution caller. template <typename DistrT, typename... Args> @@ -69,12 +55,13 @@ static typename DistrT::result_type Impl(std::true_type, URBG* urbg, Args&&... args) { using ResultT = typename DistrT::result_type; - using ArgTupleT = std::tuple<absl::decay_t<Args>...>; + using ArgTupleT = std::tuple<std::decay_t<Args>...>; using KeyT = ResultT(DistrT, ArgTupleT); ArgTupleT arg_tuple(std::forward<Args>(args)...); ResultT result; - if (!urbg->InvokeMock(FastTypeId<KeyT>(), &arg_tuple, &result)) { + if (!RandomMockingAccess::InvokeMock<URBG>(urbg, FastTypeId<KeyT>(), + &arg_tuple, &result)) { auto dist = absl::make_from_tuple<DistrT>(arg_tuple); result = dist(*urbg); } @@ -84,8 +71,8 @@ // Default implementation of distribution caller. template <typename DistrT, typename... Args> static typename DistrT::result_type Call(URBG* urbg, Args&&... args) { - return Impl<DistrT, Args...>(HasInvokeMock{}, urbg, - std::forward<Args>(args)...); + return Impl<DistrT, Args...>(RandomMockingAccess::HasInvokeMock<URBG>{}, + urbg, std::forward<Args>(args)...); } };
diff --git a/absl/random/internal/fast_uniform_bits.h b/absl/random/internal/fast_uniform_bits.h index 83ee5c0..7292d1f 100644 --- a/absl/random/internal/fast_uniform_bits.h +++ b/absl/random/internal/fast_uniform_bits.h
@@ -126,7 +126,7 @@ static_assert((URBG::max)() > (URBG::min)(), "URBG::max and URBG::min may not be equal."); - using tag = absl::conditional_t<IsPowerOfTwoOrZero(RangeSize<URBG>()), + using tag = std::conditional_t<IsPowerOfTwoOrZero(RangeSize<URBG>()), SimplifiedLoopTag, RejectionLoopTag>; return Generate(g, tag{}); }
diff --git a/absl/random/internal/generate_real.h b/absl/random/internal/generate_real.h index 9a6f400..fc35240 100644 --- a/absl/random/internal/generate_real.h +++ b/absl/random/internal/generate_real.h
@@ -69,7 +69,7 @@ bool IncludeZero = true> inline RealType GenerateRealFromBits(uint64_t bits, int exp_bias = 0) { using real_type = RealType; - using uint_type = absl::conditional_t<std::is_same<real_type, float>::value, + using uint_type = std::conditional_t<std::is_same<real_type, float>::value, uint32_t, uint64_t>; static_assert(
diff --git a/absl/random/internal/iostream_state_saver.h b/absl/random/internal/iostream_state_saver.h index 0f56bcb..5556b67 100644 --- a/absl/random/internal/iostream_state_saver.h +++ b/absl/random/internal/iostream_state_saver.h
@@ -95,7 +95,7 @@ } template <typename T> -typename absl::enable_if_t<!std::is_base_of<std::ios_base, T>::value, +typename std::enable_if_t<!std::is_base_of<std::ios_base, T>::value, null_state_saver<T>> make_ostream_state_saver(T& is, // NOLINT(runtime/references) std::ios_base::fmtflags flags = std::ios_base::dec) { @@ -159,7 +159,7 @@ } template <typename T> -typename absl::enable_if_t<!std::is_base_of<std::ios_base, T>::value, +typename std::enable_if_t<!std::is_base_of<std::ios_base, T>::value, null_state_saver<T>> make_istream_state_saver(T& is, // NOLINT(runtime/references) std::ios_base::fmtflags flags = std::ios_base::dec) {
diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc index ea9d2af..3c1db21 100644 --- a/absl/random/internal/iostream_state_saver_test.cc +++ b/absl/random/internal/iostream_state_saver_test.cc
@@ -19,6 +19,7 @@ #include <sstream> #include <string> +#include <type_traits> #include "gtest/gtest.h" @@ -29,7 +30,7 @@ using absl::random_internal::stream_precision_helper; template <typename T> -typename absl::enable_if_t<std::is_integral<T>::value, T> // +typename std::enable_if_t<std::is_integral<T>::value, T> // StreamRoundTrip(T t) { std::stringstream ss; { @@ -53,7 +54,7 @@ } template <typename T> -typename absl::enable_if_t<std::is_floating_point<T>::value, T> // +typename std::enable_if_t<std::is_floating_point<T>::value, T> // StreamRoundTrip(T t) { std::stringstream ss; {
diff --git a/absl/random/internal/mock_helpers.h b/absl/random/internal/mock_helpers.h index 2d66a3b..b73b9cb 100644 --- a/absl/random/internal/mock_helpers.h +++ b/absl/random/internal/mock_helpers.h
@@ -21,7 +21,7 @@ #include "absl/base/config.h" #include "absl/base/fast_type_id.h" -#include "absl/types/optional.h" +#include "absl/random/mocking_access.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -50,6 +50,7 @@ // class MockHelpers { using IdType = ::absl::FastTypeIdType; + using RandomMockingAccess = ::absl::RandomMockingAccess; // Given a key signature type used to index the mock, extract the components. // KeyT is expected to have the form: @@ -64,39 +65,7 @@ using arg_tuple_type = ArgTupleT; }; - // Detector for InvokeMock. - template <class T> - using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock( - std::declval<IdType>(), std::declval<void*>(), std::declval<void*>())); - - // Empty implementation of InvokeMock. - template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG, - typename... Args> - static std::optional<ReturnT> InvokeMockImpl(char, URBG*, Args&&...) { - return std::nullopt; - } - - // Non-empty implementation of InvokeMock. - template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG, - typename = invoke_mock_t<URBG>, typename... Args> - static std::optional<ReturnT> InvokeMockImpl(int, URBG* urbg, - Args&&... args) { - ArgTupleT arg_tuple(std::forward<Args>(args)...); - ReturnT result; - if (urbg->InvokeMock(FastTypeId<KeyT>(), &arg_tuple, &result)) { - return result; - } - return std::nullopt; - } - public: - // InvokeMock is private; this provides access for some specialized use cases. - template <typename URBG> - static inline bool PrivateInvokeMock(URBG* urbg, IdType key_id, - void* args_tuple, void* result) { - return urbg->InvokeMock(key_id, args_tuple, result); - } - // Invoke a mock for the KeyT (may or may not be a signature). // // KeyT is used to generate a typeid-based lookup key for the mock. @@ -110,12 +79,16 @@ template <typename KeyT, typename URBG, typename... Args> static auto MaybeInvokeMock(URBG* urbg, Args&&... args) -> std::optional<typename KeySignature<KeyT>::result_type> { - // Use function overloading to dispatch to the implementation since - // more modern patterns (e.g. require + constexpr) are not supported in all - // compiler configurations. - return InvokeMockImpl<KeyT, typename KeySignature<KeyT>::result_type, - typename KeySignature<KeyT>::arg_tuple_type, URBG>( - 0, urbg, std::forward<Args>(args)...); + if constexpr (RandomMockingAccess::HasInvokeMock<URBG>::value) { + typename KeySignature<KeyT>::arg_tuple_type arg_tuple( + std::forward<Args>(args)...); + typename KeySignature<KeyT>::result_type result; + if (RandomMockingAccess::InvokeMock(urbg, FastTypeId<KeyT>(), &arg_tuple, + &result)) { + return result; + } + } + return std::nullopt; } // Acquire a mock for the KeyT (may or may not be a signature), set up to use
diff --git a/absl/random/internal/nonsecure_base.h b/absl/random/internal/nonsecure_base.h index e8c2bb9..903dbd9 100644 --- a/absl/random/internal/nonsecure_base.h +++ b/absl/random/internal/nonsecure_base.h
@@ -78,7 +78,7 @@ // std::contiguous_iterator_tag provides a mechanism for testing this // capability, however until Abseil's support requirements allow us to // assume C++20, limit checks to a few common cases. - using TagType = absl::conditional_t< + using TagType = std::conditional_t< (std::is_pointer<RandomAccessIterator>::value || std::is_same<RandomAccessIterator, typename std::vector<U>::iterator>::value), @@ -106,7 +106,7 @@ NonsecureURBGBase& operator=(NonsecureURBGBase&&) = default; // Constructor using a seed - template <class SSeq, typename = typename absl::enable_if_t< + template <class SSeq, typename = typename std::enable_if_t< !std::is_same<SSeq, NonsecureURBGBase>::value>> explicit NonsecureURBGBase(SSeq&& seq) : urbg_(ConstructURBG(std::forward<SSeq>(seq))) {}
diff --git a/absl/random/internal/nonsecure_base_test.cc b/absl/random/internal/nonsecure_base_test.cc index 6e3e712..3795563 100644 --- a/absl/random/internal/nonsecure_base_test.cc +++ b/absl/random/internal/nonsecure_base_test.cc
@@ -77,13 +77,13 @@ static_assert(!std::is_copy_constructible<E>::value, "NonsecureURBGBase should not be copy constructible"); - static_assert(!absl::is_copy_assignable<E>::value, + static_assert(!std::is_copy_assignable<E>::value, "NonsecureURBGBase should not be copy assignable"); static_assert(std::is_move_constructible<E>::value, "NonsecureURBGBase should be move constructible"); - static_assert(absl::is_move_assignable<E>::value, + static_assert(std::is_move_assignable<E>::value, "NonsecureURBGBase should be move assignable"); static_assert(std::is_same<decltype(std::declval<E>()()), T>::value,
diff --git a/absl/random/internal/pcg_engine.h b/absl/random/internal/pcg_engine.h index e1f4ef3..2f41d58 100644 --- a/absl/random/internal/pcg_engine.h +++ b/absl/random/internal/pcg_engine.h
@@ -67,7 +67,7 @@ explicit pcg_engine(uint64_t seed_value = 0) { seed(seed_value); } template <class SeedSequence, - typename = typename absl::enable_if_t< + typename = typename std::enable_if_t< !std::is_same<SeedSequence, pcg_engine>::value>> explicit pcg_engine(SeedSequence&& seq) { seed(seq); @@ -90,7 +90,7 @@ } template <class SeedSequence> - typename absl::enable_if_t< + typename std::enable_if_t< !std::is_convertible<SeedSequence, uint64_t>::value, void> seed(SeedSequence&& seq) { reseed(seq); @@ -105,7 +105,7 @@ bool operator!=(const pcg_engine& other) const { return !(*this == other); } template <class CharT, class Traits> - friend typename absl::enable_if_t<(sizeof(state_type) == 16), + friend typename std::enable_if_t<(sizeof(state_type) == 16), std::basic_ostream<CharT, Traits>&> operator<<( std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) @@ -121,7 +121,7 @@ } template <class CharT, class Traits> - friend typename absl::enable_if_t<(sizeof(state_type) <= 8), + friend typename std::enable_if_t<(sizeof(state_type) <= 8), std::basic_ostream<CharT, Traits>&> operator<<( std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) @@ -134,7 +134,7 @@ } template <class CharT, class Traits> - friend typename absl::enable_if_t<(sizeof(state_type) == 16), + friend typename std::enable_if_t<(sizeof(state_type) == 16), std::basic_istream<CharT, Traits>&> operator>>( std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) @@ -155,7 +155,7 @@ } template <class CharT, class Traits> - friend typename absl::enable_if_t<(sizeof(state_type) <= 8), + friend typename std::enable_if_t<(sizeof(state_type) <= 8), std::basic_istream<CharT, Traits>&> operator>>( std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references)
diff --git a/absl/random/internal/pcg_engine_test.cc b/absl/random/internal/pcg_engine_test.cc index 4d763e8..211a849 100644 --- a/absl/random/internal/pcg_engine_test.cc +++ b/absl/random/internal/pcg_engine_test.cc
@@ -18,6 +18,7 @@ #include <bitset> #include <random> #include <sstream> +#include <type_traits> #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -169,13 +170,13 @@ static_assert(std::is_copy_constructible<E>::value, "engine_type must be copy constructible"); - static_assert(absl::is_copy_assignable<E>::value, + static_assert(std::is_copy_assignable<E>::value, "engine_type must be copy assignable"); static_assert(std::is_move_constructible<E>::value, "engine_type must be move constructible"); - static_assert(absl::is_move_assignable<E>::value, + static_assert(std::is_move_assignable<E>::value, "engine_type must be move assignable"); static_assert(std::is_same<decltype(std::declval<E>()()), T>::value,
diff --git a/absl/random/internal/randen_engine.h b/absl/random/internal/randen_engine.h index 925e1bb..4e4fef9 100644 --- a/absl/random/internal/randen_engine.h +++ b/absl/random/internal/randen_engine.h
@@ -63,7 +63,7 @@ explicit randen_engine(result_type seed_value) { seed(seed_value); } template <class SeedSequence, - typename = typename absl::enable_if_t< + typename = typename std::enable_if_t< !std::is_same<SeedSequence, randen_engine>::value>> explicit randen_engine(SeedSequence&& seq) { seed(seq); @@ -93,7 +93,7 @@ } template <class SeedSequence> - typename absl::enable_if_t< + typename std::enable_if_t< !std::is_convertible<SeedSequence, result_type>::value> seed(SeedSequence&& seq) { // Zeroes the state.
diff --git a/absl/random/internal/randen_engine_test.cc b/absl/random/internal/randen_engine_test.cc index 122d90b..85c0a2b 100644 --- a/absl/random/internal/randen_engine_test.cc +++ b/absl/random/internal/randen_engine_test.cc
@@ -18,6 +18,7 @@ #include <bitset> #include <random> #include <sstream> +#include <type_traits> #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -170,13 +171,13 @@ static_assert(std::is_copy_constructible<E>::value, "randen_engine must be copy constructible"); - static_assert(absl::is_copy_assignable<E>::value, + static_assert(std::is_copy_assignable<E>::value, "randen_engine must be copy assignable"); static_assert(std::is_move_constructible<E>::value, "randen_engine must be move constructible"); - static_assert(absl::is_move_assignable<E>::value, + static_assert(std::is_move_assignable<E>::value, "randen_engine must be move assignable"); static_assert(std::is_same<decltype(std::declval<E>()()), T>::value,
diff --git a/absl/random/internal/randen_test.cc b/absl/random/internal/randen_test.cc index 92773b8..cf94970 100644 --- a/absl/random/internal/randen_test.cc +++ b/absl/random/internal/randen_test.cc
@@ -15,6 +15,7 @@ #include "absl/random/internal/randen.h" #include <cstring> +#include <type_traits> #include "gtest/gtest.h" #include "absl/meta/type_traits.h" @@ -27,13 +28,13 @@ static_assert(std::is_copy_constructible<Randen>::value, "Randen must be copy constructible"); - static_assert(absl::is_copy_assignable<Randen>::value, + static_assert(std::is_copy_assignable<Randen>::value, "Randen must be copy assignable"); static_assert(std::is_move_constructible<Randen>::value, "Randen must be move constructible"); - static_assert(absl::is_move_assignable<Randen>::value, + static_assert(std::is_move_assignable<Randen>::value, "Randen must be move assignable"); }
diff --git a/absl/random/internal/salted_seed_seq.h b/absl/random/internal/salted_seed_seq.h index c374d93..a86c39d 100644 --- a/absl/random/internal/salted_seed_seq.h +++ b/absl/random/internal/salted_seed_seq.h
@@ -72,7 +72,7 @@ // The common case is that generate is called with ContiguousIterators // to uint arrays. Such contiguous memory regions may be optimized, // which we detect here. - using TagType = absl::conditional_t< + using TagType = std::conditional_t< (std::is_same<U, uint32_t>::value && (std::is_pointer<RandomAccessIterator>::value || std::is_same<RandomAccessIterator, @@ -142,14 +142,14 @@ // non-salted seed parameters. template < typename SSeq, // - typename EnableIf = absl::enable_if_t<is_salted_seed_seq<SSeq>::value>> + typename EnableIf = std::enable_if_t<is_salted_seed_seq<SSeq>::value>> SSeq MakeSaltedSeedSeq(SSeq&& seq) { return SSeq(std::forward<SSeq>(seq)); } template < typename SSeq, // - typename EnableIf = absl::enable_if_t<!is_salted_seed_seq<SSeq>::value>> + typename EnableIf = std::enable_if_t<!is_salted_seed_seq<SSeq>::value>> SaltedSeedSeq<typename std::decay<SSeq>::type> MakeSaltedSeedSeq(SSeq&& seq) { using sseq_type = typename std::decay<SSeq>::type; using result_type = typename sseq_type::result_type;
diff --git a/absl/random/internal/traits.h b/absl/random/internal/traits.h index f874a0f..f23bcd2 100644 --- a/absl/random/internal/traits.h +++ b/absl/random/internal/traits.h
@@ -20,6 +20,7 @@ #include <type_traits> #include "absl/base/config.h" +#include "absl/meta/type_traits.h" #include "absl/numeric/bits.h" #include "absl/numeric/int128.h" @@ -27,6 +28,26 @@ ABSL_NAMESPACE_BEGIN namespace random_internal { +// is_urbg<URBG> +// +// Indicates whether a type URBG is a Uniform Random Bit Generator. +template <typename URBG, typename = void, typename = void, typename = void> +struct is_urbg : std::false_type {}; + +template <typename URBG> +struct is_urbg< + URBG, + std::enable_if_t<std::is_same< + typename URBG::result_type, + typename std::decay<decltype((URBG::min)())>::type>::value>, + std::enable_if_t<std::is_same< + typename URBG::result_type, + typename std::decay<decltype((URBG::max)())>::type>::value>, + std::enable_if_t<std::is_same< + typename URBG::result_type, + typename std::decay<decltype(std::declval<URBG>()())>::type>::value>> + : std::true_type {}; + // random_internal::is_widening_convertible<A, B> // // Returns whether a type A is widening-convertible to a type B.
diff --git a/absl/random/internal/traits_test.cc b/absl/random/internal/traits_test.cc index 2164582..100fd9b 100644 --- a/absl/random/internal/traits_test.cc +++ b/absl/random/internal/traits_test.cc
@@ -15,14 +15,19 @@ #include "absl/random/internal/traits.h" #include <cstdint> +#include <random> #include <type_traits> #include "gtest/gtest.h" namespace { +using absl::random_internal::is_urbg; using absl::random_internal::is_widening_convertible; +static_assert(is_urbg<std::minstd_rand>::value); +static_assert(!is_urbg<uint64_t>::value); + // CheckWideningConvertsToSelf<T1, T2, ...>() // // For each type T, checks:
diff --git a/absl/random/internal/uniform_helper.h b/absl/random/internal/uniform_helper.h index d230073..f50be61 100644 --- a/absl/random/internal/uniform_helper.h +++ b/absl/random/internal/uniform_helper.h
@@ -76,7 +76,7 @@ // Return-type for absl::Uniform() when the return-type is inferred. template <typename A, typename B> using uniform_inferred_return_t = - absl::enable_if_t<absl::disjunction<is_widening_convertible<A, B>, + std::enable_if_t<std::disjunction<is_widening_convertible<A, B>, is_widening_convertible<B, A>>::value, typename std::conditional< is_widening_convertible<A, B>::value, B, A>::type>; @@ -98,10 +98,10 @@ // uniform_upper_bound(IntervalOpenClosed, a, b)] // template <typename IntType, typename Tag> -typename absl::enable_if_t< - absl::conjunction< +typename std::enable_if_t< + std::conjunction< IsIntegral<IntType>, - absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>, + std::disjunction<std::is_same<Tag, IntervalOpenClosedTag>, std::is_same<Tag, IntervalOpenOpenTag>>>::value, IntType> uniform_lower_bound(Tag, IntType a, IntType) { @@ -109,10 +109,10 @@ } template <typename FloatType, typename Tag> -typename absl::enable_if_t< - absl::conjunction< +typename std::enable_if_t< + std::conjunction< std::is_floating_point<FloatType>, - absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>, + std::disjunction<std::is_same<Tag, IntervalOpenClosedTag>, std::is_same<Tag, IntervalOpenOpenTag>>>::value, FloatType> uniform_lower_bound(Tag, FloatType a, FloatType b) { @@ -120,8 +120,8 @@ } template <typename NumType, typename Tag> -typename absl::enable_if_t< - absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, +typename std::enable_if_t< + std::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, std::is_same<Tag, IntervalClosedOpenTag>>::value, NumType> uniform_lower_bound(Tag, NumType a, NumType) { @@ -129,10 +129,10 @@ } template <typename IntType, typename Tag> -typename absl::enable_if_t< - absl::conjunction< +typename std::enable_if_t< + std::conjunction< IsIntegral<IntType>, - absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>, + std::disjunction<std::is_same<Tag, IntervalClosedOpenTag>, std::is_same<Tag, IntervalOpenOpenTag>>>::value, IntType> uniform_upper_bound(Tag, IntType, IntType b) { @@ -140,10 +140,10 @@ } template <typename FloatType, typename Tag> -typename absl::enable_if_t< - absl::conjunction< +typename std::enable_if_t< + std::conjunction< std::is_floating_point<FloatType>, - absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>, + std::disjunction<std::is_same<Tag, IntervalClosedOpenTag>, std::is_same<Tag, IntervalOpenOpenTag>>>::value, FloatType> uniform_upper_bound(Tag, FloatType, FloatType b) { @@ -151,10 +151,10 @@ } template <typename IntType, typename Tag> -typename absl::enable_if_t< - absl::conjunction< +typename std::enable_if_t< + std::conjunction< IsIntegral<IntType>, - absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, + std::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, std::is_same<Tag, IntervalOpenClosedTag>>>::value, IntType> uniform_upper_bound(Tag, IntType, IntType b) { @@ -162,10 +162,10 @@ } template <typename FloatType, typename Tag> -typename absl::enable_if_t< - absl::conjunction< +typename std::enable_if_t< + std::conjunction< std::is_floating_point<FloatType>, - absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, + std::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, std::is_same<Tag, IntervalOpenClosedTag>>>::value, FloatType> uniform_upper_bound(Tag, FloatType, FloatType b) { @@ -195,13 +195,13 @@ // (0, 0] is not legal, but (0, 0+epsilon] is. // template <typename FloatType> -absl::enable_if_t<std::is_floating_point<FloatType>::value, bool> +std::enable_if_t<std::is_floating_point<FloatType>::value, bool> is_uniform_range_valid(FloatType a, FloatType b) { return a <= b && std::isfinite(b - a); } template <typename IntType> -absl::enable_if_t<IsIntegral<IntType>::value, bool> is_uniform_range_valid( +std::enable_if_t<IsIntegral<IntType>::value, bool> is_uniform_range_valid( IntType a, IntType b) { return a <= b; }
diff --git a/absl/random/internal/uniform_helper_test.cc b/absl/random/internal/uniform_helper_test.cc index 173c49b..5b20b0b 100644 --- a/absl/random/internal/uniform_helper_test.cc +++ b/absl/random/internal/uniform_helper_test.cc
@@ -17,6 +17,7 @@ #include <cmath> #include <cstdint> #include <random> +#include <type_traits> #include "gtest/gtest.h" @@ -214,7 +215,7 @@ template <typename A, typename B, typename Expect> void CheckArgsInferType() { static_assert( - absl::conjunction< + std::conjunction< std::is_same<Expect, decltype(InferredUniformReturnT<A, B>(0))>, std::is_same<Expect, decltype(InferredUniformReturnT<B, A>(0))>>::value,
diff --git a/absl/random/mocking_access.h b/absl/random/mocking_access.h new file mode 100644 index 0000000..eb2f98b --- /dev/null +++ b/absl/random/mocking_access.h
@@ -0,0 +1,74 @@ +// Copyright 2019 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. + +#ifndef ABSL_RANDOM_MOCKING_ACCESS_H_ +#define ABSL_RANDOM_MOCKING_ACCESS_H_ + +#include <type_traits> +#include <utility> + +#include "absl/base/config.h" +#include "absl/base/fast_type_id.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// RandomMockingAccess must be a friend of any class which exposes an InvokeMock +// method. All calls to InvokeMock should be made through RandomMockingAccess. +// +// Any URBG type which wants to participate in mocking should declare +// RandomMockingAccess as a friend and have a protected or private method with +// the signature: +// +// bool InvokeMock(absl::FastTypeIdType key_id, void* args_tuple, void* result) +// +// This method returns false when mocking is not enabled, otherwise it will +// apply the mock. Typically this will involve forwarding to an underlying +// URBG such as absl::MockingBitGen by calling RandomMockingAccess::InvokeMock +// after checking that RandomMockingAccess::HasInvokeMock<URBG> is true for the +// underlying URBG type. +class RandomMockingAccess { + template <template <class...> class Trait, class AlwaysVoid, class... Args> + struct detector : std::false_type {}; + template <template <class...> class Trait, class... Args> + struct detector<Trait, absl::void_t<Trait<Args...>>, Args...> + : std::true_type {}; + + using IdType = ::absl::FastTypeIdType; + + // Detector for `bool InvokeMock(key_id, args_tuple*, result*)` + // Lives inside RandomMockingAccess so that it has friend access to private + // members of URBG types. + template <class T> + using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock( + std::declval<IdType>(), std::declval<void*>(), std::declval<void*>())); + + public: + // Returns true if the URBG type has an InvokeMock method. + template <typename T> + using HasInvokeMock = typename detector<invoke_mock_t, void, T>::type; + + // InvokeMock is private; calls to InvokeMock are proxied by MockingAccess. + template <typename URBG> + static inline bool InvokeMock(URBG* urbg, IdType key_id, void* args_tuple, + void* result) { + return urbg->InvokeMock(key_id, args_tuple, result); + } +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_RANDOM_MOCKING_ACCESS_H_
diff --git a/absl/random/mocking_bit_gen.h b/absl/random/mocking_bit_gen.h index 1680ff4..74aa17d 100644 --- a/absl/random/mocking_bit_gen.h +++ b/absl/random/mocking_bit_gen.h
@@ -39,20 +39,13 @@ #include "absl/container/flat_hash_map.h" #include "absl/meta/type_traits.h" #include "absl/random/internal/mock_helpers.h" +#include "absl/random/mocking_access.h" #include "absl/random/random.h" #include "absl/utility/utility.h" namespace absl { ABSL_NAMESPACE_BEGIN -class BitGenRef; - -namespace random_internal { -template <typename> -struct DistributionCaller; -class MockHelpers; -} // namespace random_internal - // MockingBitGen // // `absl::MockingBitGen` is a mock Uniform Random Bit Generator (URBG) class @@ -181,13 +174,13 @@ using MockFnType = decltype(GetMockFnType(std::declval<ResultT>(), std::declval<ArgTupleT>())); - using WrappedFnType = absl::conditional_t< + using WrappedFnType = std::conditional_t< std::is_same<SelfT, ::testing::NiceMock<MockingBitGen>>::value, ::testing::NiceMock<MockFnType>, - absl::conditional_t< + std::conditional_t< std::is_same<SelfT, ::testing::NaggyMock<MockingBitGen>>::value, ::testing::NaggyMock<MockFnType>, - absl::conditional_t< + std::conditional_t< std::is_same<SelfT, ::testing::StrictMock<MockingBitGen>>::value, ::testing::StrictMock<MockFnType>, MockFnType>>>; @@ -224,11 +217,8 @@ absl::flat_hash_map<FastTypeIdType, std::unique_ptr<FunctionHolder>> mocks_; absl::BitGen gen_; - template <typename> - friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock - friend class ::absl::BitGenRef; // for InvokeMock - friend class ::absl::random_internal::MockHelpers; // for RegisterMock, - // InvokeMock + friend class ::absl::RandomMockingAccess; // for InvokeMock + friend class ::absl::random_internal::MockHelpers; // for RegisterMock }; ABSL_NAMESPACE_END
diff --git a/absl/random/uniform_real_distribution.h b/absl/random/uniform_real_distribution.h index 8bef946..e5ea6f2 100644 --- a/absl/random/uniform_real_distribution.h +++ b/absl/random/uniform_real_distribution.h
@@ -159,7 +159,7 @@ using random_internal::GeneratePositiveTag; using random_internal::GenerateRealFromBits; using real_type = - absl::conditional_t<std::is_same<RealType, float>::value, float, double>; + std::conditional_t<std::is_same<RealType, float>::value, float, double>; while (true) { const result_type sample =
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel index 038f1da..dce316b 100644 --- a/absl/status/BUILD.bazel +++ b/absl/status/BUILD.bazel
@@ -65,11 +65,13 @@ "//absl/debugging:stacktrace", "//absl/debugging:symbolize", "//absl/functional:function_ref", + "//absl/hash", "//absl/memory", "//absl/strings", "//absl/strings:cord", "//absl/strings:str_format", "//absl/types:optional", + "//absl/types:source_location", "//absl/types:span", ], ) @@ -84,6 +86,7 @@ "//absl/strings", "//absl/strings:cord", "//absl/strings:str_format", + "//absl/types:source_location", "@googletest//:gtest", "@googletest//:gtest_main", ], @@ -96,6 +99,7 @@ tags = ["benchmark"], deps = [ ":status", + "//absl/types:source_location", "@google_benchmark//:benchmark_main", ], ) @@ -122,6 +126,8 @@ "//absl/strings", "//absl/strings:has_ostream_operator", "//absl/strings:str_format", + "//absl/types:source_location", + "//absl/types:span", "//absl/types:variant", "//absl/utility", ], @@ -139,6 +145,7 @@ "//absl/memory", "//absl/strings", "//absl/types:any", + "//absl/types:source_location", "//absl/types:variant", "//absl/utility", "@googletest//:gtest",
diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt index a418bd9..8d519a5 100644 --- a/absl/status/CMakeLists.txt +++ b/absl/status/CMakeLists.txt
@@ -41,6 +41,7 @@ absl::nullability absl::optional absl::raw_logging_internal + absl::source_location absl::span absl::stacktrace absl::str_format @@ -59,6 +60,7 @@ ${ABSL_TEST_COPTS} DEPS absl::status + absl::source_location absl::str_format absl::strings GTest::gmock_main @@ -82,6 +84,7 @@ absl::nullability absl::raw_logging_internal absl::status + absl::source_location absl::str_format absl::strings absl::type_traits @@ -101,6 +104,7 @@ absl::status absl::status_matchers absl::statusor + absl::source_location absl::strings GTest::gmock_main )
diff --git a/absl/status/internal/status_internal.cc b/absl/status/internal/status_internal.cc index 74bece4..6e79234 100644 --- a/absl/status/internal/status_internal.cc +++ b/absl/status/internal/status_internal.cc
@@ -32,6 +32,7 @@ #include "absl/debugging/leak_check.h" #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" +#include "absl/hash/hash.h" #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/status/status_payload_printer.h" @@ -41,6 +42,8 @@ #include "absl/strings/str_format.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" +#include "absl/types/source_location.h" +#include "absl/types/span.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -130,6 +133,14 @@ } } +absl::Span<const SourceLocation> StatusRep::GetSourceLocations() const { + return absl::MakeSpan(source_locations_); +} + +void StatusRep::AddSourceLocation(absl::SourceLocation loc) { + source_locations_.push_back(loc); +} + std::string StatusRep::ToString(StatusToStringMode mode) const { std::string text; absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message()); @@ -150,6 +161,17 @@ "']"); }); } + const bool with_source_location = + (mode & StatusToStringMode::kWithSourceLocation) == + StatusToStringMode::kWithSourceLocation; + if (with_source_location && !source_locations_.empty()) { + absl::string_view whitespace = (absl::Hash<int>{}(42) % 2 == 0) ? "" : " "; + absl::StrAppend(&text, "\n=== Source Location Trace: ===", whitespace, + "\n"); + for (const absl::SourceLocation loc : GetSourceLocations()) { + absl::StrAppend(&text, loc.file_name(), ":", loc.line(), "\n"); + } + } return text; } @@ -204,6 +226,7 @@ payloads = absl::make_unique<status_internal::Payloads>(*payloads_); } auto* new_rep = new StatusRep(code_, message_, std::move(payloads)); + new_rep->source_locations_ = source_locations_; Unref(); return new_rep; }
diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h index f2bd83a..cafe160 100644 --- a/absl/status/internal/status_internal.h +++ b/absl/status/internal/status_internal.h
@@ -17,10 +17,10 @@ #include <atomic> #include <cstdint> #include <memory> +#include <optional> #include <string> #include <utility> - -#include <optional> +#include <vector> #include "absl/base/attributes.h" #include "absl/base/config.h" @@ -29,6 +29,8 @@ #include "absl/strings/cord.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "absl/types/source_location.h" +#include "absl/types/span.h" #ifndef SWIG // Disabled for SWIG as it doesn't parse attributes correctly. @@ -57,6 +59,9 @@ enum class StatusToStringMode : int; namespace status_internal { +#ifndef SWIG +class StatusPrivateAccessor; +#endif // !SWIG // Container for status payloads. struct Payload { @@ -96,6 +101,9 @@ absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor) const; + absl::Span<const SourceLocation> GetSourceLocations() const; + void AddSourceLocation(absl::SourceLocation loc); + std::string ToString(StatusToStringMode mode) const; bool operator==(const StatusRep& other) const; @@ -114,6 +122,7 @@ // is non-empty, then the resulting string_view is null terminated. // This is required to implement 'StatusMessageAsCStr(...)' std::string message_; + absl::InlinedVector<absl::SourceLocation, 1> source_locations_; std::unique_ptr<status_internal::Payloads> payloads_; };
diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h index 8a4e2f5..ccfdedb 100644 --- a/absl/status/internal/statusor_internal.h +++ b/absl/status/internal/statusor_internal.h
@@ -60,7 +60,7 @@ // Detects whether `T` is constructible or convertible from `StatusOr<U>`. template <typename T, typename U> using IsConstructibleOrConvertibleFromStatusOr = - absl::disjunction<std::is_constructible<T, StatusOr<U>&>, + std::disjunction<std::is_constructible<T, StatusOr<U>&>, std::is_constructible<T, const StatusOr<U>&>, std::is_constructible<T, StatusOr<U>&&>, std::is_constructible<T, const StatusOr<U>&&>, @@ -73,7 +73,7 @@ // `StatusOr<U>`. template <typename T, typename U> using IsConstructibleOrConvertibleOrAssignableFromStatusOr = - absl::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>, + std::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>, std::is_assignable<T&, StatusOr<U>&>, std::is_assignable<T&, const StatusOr<U>&>, std::is_assignable<T&, StatusOr<U>&&>, @@ -83,7 +83,7 @@ // when `U` is `StatusOr<V>` and `T` is constructible or convertible from `V`. template <typename T, typename U> struct IsDirectInitializationAmbiguous - : public absl::conditional_t< + : public std::conditional_t< std::is_same<absl::remove_cvref_t<U>, U>::value, std::false_type, IsDirectInitializationAmbiguous<T, absl::remove_cvref_t<U>>> {}; @@ -95,7 +95,7 @@ // temporaries. // REQUIRES: T and U are references. template <typename T, typename U> -using IsReferenceConversionValid = absl::conjunction< // +using IsReferenceConversionValid = std::conjunction< // std::is_reference<T>, std::is_reference<U>, // The references are convertible. This checks for // lvalue/rvalue compatibility. @@ -108,13 +108,13 @@ // Checks against the constraints of the direction initialization, i.e. when // `StatusOr<T>::StatusOr(U&&)` should participate in overload resolution. template <typename T, typename U> -using IsDirectInitializationValid = absl::disjunction< +using IsDirectInitializationValid = std::disjunction< // Short circuits if T is basically U. std::is_same<T, absl::remove_cvref_t<U>>, // std::conditional_t< std::is_reference_v<T>, // IsReferenceConversionValid<T, U>, - absl::negation<absl::disjunction< + std::negation<std::disjunction< std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>, std::is_same<absl::Status, absl::remove_cvref_t<U>>, std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>, @@ -132,7 +132,7 @@ // s1 = s2; // ambiguous, `s1 = s2.ValueOrDie()` or `s1 = bool(s2)`? template <typename T, typename U> struct IsForwardingAssignmentAmbiguous - : public absl::conditional_t< + : public std::conditional_t< std::is_same<absl::remove_cvref_t<U>, U>::value, std::false_type, IsForwardingAssignmentAmbiguous<T, absl::remove_cvref_t<U>>> {}; @@ -143,91 +143,91 @@ // Checks against the constraints of the forwarding assignment, i.e. whether // `StatusOr<T>::operator(U&&)` should participate in overload resolution. template <typename T, typename U> -using IsForwardingAssignmentValid = absl::disjunction< +using IsForwardingAssignmentValid = std::disjunction< // Short circuits if T is basically U. std::is_same<T, absl::remove_cvref_t<U>>, - absl::negation<absl::disjunction< + std::negation<std::disjunction< std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>, std::is_same<absl::Status, absl::remove_cvref_t<U>>, std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>, IsForwardingAssignmentAmbiguous<T, U>>>>; template <bool Value, typename T> -using Equality = std::conditional_t<Value, T, absl::negation<T>>; +using Equality = std::conditional_t<Value, T, std::negation<T>>; template <bool Explicit, typename T, typename U, bool Lifetimebound> -using IsConstructionValid = absl::conjunction< +using IsConstructionValid = std::conjunction< Equality<Lifetimebound, - absl::disjunction< + std::disjunction< std::is_reference<T>, type_traits_internal::IsLifetimeBoundAssignment<T, U>>>, IsDirectInitializationValid<T, U&&>, std::is_constructible<T, U&&>, Equality<!Explicit, std::is_convertible<U&&, T>>, - absl::disjunction< + std::disjunction< std::is_same<T, absl::remove_cvref_t<U>>, - absl::conjunction< + std::conjunction< std::conditional_t< Explicit, - absl::negation<std::is_constructible<absl::Status, U&&>>, - absl::negation<std::is_convertible<U&&, absl::Status>>>, - absl::negation< + std::negation<std::is_constructible<absl::Status, U&&>>, + std::negation<std::is_convertible<U&&, absl::Status>>>, + std::negation< internal_statusor::HasConversionOperatorToStatusOr<T, U&&>>>>>; template <typename T, typename U, bool Lifetimebound> -using IsAssignmentValid = absl::conjunction< +using IsAssignmentValid = std::conjunction< Equality<Lifetimebound, - absl::disjunction< + std::disjunction< std::is_reference<T>, type_traits_internal::IsLifetimeBoundAssignment<T, U>>>, std::conditional_t<std::is_reference_v<T>, IsReferenceConversionValid<T, U&&>, - absl::conjunction<std::is_constructible<T, U&&>, + std::conjunction<std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>>>, - absl::disjunction< + std::disjunction< std::is_same<T, absl::remove_cvref_t<U>>, - absl::conjunction< - absl::negation<std::is_convertible<U&&, absl::Status>>, - absl::negation<HasConversionOperatorToStatusOr<T, U&&>>>>, + std::conjunction< + std::negation<std::is_convertible<U&&, absl::Status>>, + std::negation<HasConversionOperatorToStatusOr<T, U&&>>>>, IsForwardingAssignmentValid<T, U&&>>; template <bool Explicit, typename T, typename U> -using IsConstructionFromStatusValid = absl::conjunction< - absl::negation<std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>>, - absl::negation<std::is_same<T, absl::remove_cvref_t<U>>>, - absl::negation<std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>>, +using IsConstructionFromStatusValid = std::conjunction< + std::negation<std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>>, + std::negation<std::is_same<T, absl::remove_cvref_t<U>>>, + std::negation<std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>>, Equality<!Explicit, std::is_convertible<U, absl::Status>>, std::is_constructible<absl::Status, U>, - absl::negation<HasConversionOperatorToStatusOr<T, U>>>; + std::negation<HasConversionOperatorToStatusOr<T, U>>>; template <bool Explicit, typename T, typename U, bool Lifetimebound, typename UQ> -using IsConstructionFromStatusOrValid = absl::conjunction< - absl::negation<std::is_same<T, U>>, +using IsConstructionFromStatusOrValid = std::conjunction< + std::negation<std::is_same<T, U>>, // If `T` is a reference, then U must be a compatible one. - absl::disjunction<absl::negation<std::is_reference<T>>, + std::disjunction<std::negation<std::is_reference<T>>, IsReferenceConversionValid<T, U>>, Equality<Lifetimebound, type_traits_internal::IsLifetimeBoundAssignment<T, U>>, std::is_constructible<T, UQ>, Equality<!Explicit, std::is_convertible<UQ, T>>, - absl::negation<IsConstructibleOrConvertibleFromStatusOr<T, U>>>; + std::negation<IsConstructibleOrConvertibleFromStatusOr<T, U>>>; template <typename T, typename U, bool Lifetimebound> -using IsStatusOrAssignmentValid = absl::conjunction< - absl::negation<std::is_same<T, absl::remove_cvref_t<U>>>, +using IsStatusOrAssignmentValid = std::conjunction< + std::negation<std::is_same<T, absl::remove_cvref_t<U>>>, Equality<Lifetimebound, type_traits_internal::IsLifetimeBoundAssignment<T, U>>, std::is_constructible<T, U>, std::is_assignable<T, U>, - absl::negation<IsConstructibleOrConvertibleOrAssignableFromStatusOr< + std::negation<IsConstructibleOrConvertibleOrAssignableFromStatusOr< T, absl::remove_cvref_t<U>>>>; template <typename T, typename U, bool Lifetimebound> -using IsValueOrValid = absl::conjunction< +using IsValueOrValid = std::conjunction< // If `T` is a reference, then U must be a compatible one. - absl::disjunction<absl::negation<std::is_reference<T>>, + std::disjunction<std::negation<std::is_reference<T>>, IsReferenceConversionValid<T, U>>, Equality<Lifetimebound, - absl::disjunction< + std::disjunction< std::is_reference<T>, type_traits_internal::IsLifetimeBoundAssignment<T, U>>>>; @@ -331,7 +331,7 @@ } template <typename U, - absl::enable_if_t<std::is_constructible<absl::Status, U&&>::value, + std::enable_if_t<std::is_constructible<absl::Status, U&&>::value, int> = 0> explicit StatusOrData(U&& v) : status_(std::forward<U>(v)) { EnsureNotOk();
diff --git a/absl/status/status.cc b/absl/status/status.cc index 479c39f..8cd0a84 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc
@@ -37,6 +37,7 @@ #include "absl/strings/str_format.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" +#include "absl/types/source_location.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -104,11 +105,36 @@ return kMovedFrom.get(); } -Status::Status(absl::StatusCode code, absl::string_view msg) - : rep_(CodeToInlinedRep(code)) { - if (code != absl::StatusCode::kOk && !msg.empty()) { - rep_ = PointerToRep(new status_internal::StatusRep(code, msg, nullptr)); +absl::Status absl::Status::MakeNonOkStatusWithOkCode( + absl::string_view message) { + return absl::Status( + absl::Status::PointerToRep(new absl::status_internal::StatusRep( + absl::StatusCode::kOk, message, nullptr))); +} + +uintptr_t Status::MakeRep(uintptr_t inlined_rep, absl::string_view msg, + absl::SourceLocation loc) { + bool ok = inlined_rep == CodeToInlinedRep(absl::StatusCode::kOk); + if (ok) return inlined_rep; + if (msg.empty() + ) { + return inlined_rep; } + auto* rep = new status_internal::StatusRep(InlinedRepToCode(inlined_rep), msg, + nullptr); + if (loc.file_name()[0] != '\0') { + rep->AddSourceLocation(loc); + } + return PointerToRep(rep); +} + +uintptr_t Status::AddSourceLocationImpl(uintptr_t rep, + absl::SourceLocation loc) { + if (IsInlined(rep)) return rep; + if (loc.file_name()[0] == '\0') return rep; + status_internal::StatusRep* rep_ptr = PrepareToModify(rep); + rep_ptr->AddSourceLocation(loc); + return PointerToRep(rep_ptr); } status_internal::StatusRep* absl_nonnull Status::PrepareToModify( @@ -132,68 +158,74 @@ return os; } -Status AbortedError(absl::string_view message) { - return Status(absl::StatusCode::kAborted, message); +Status AbortedError(absl::string_view message, absl::SourceLocation loc) { + return Status(absl::StatusCode::kAborted, message, loc); } -Status AlreadyExistsError(absl::string_view message) { - return Status(absl::StatusCode::kAlreadyExists, message); +Status AlreadyExistsError(absl::string_view message, absl::SourceLocation loc) { + return Status(absl::StatusCode::kAlreadyExists, message, loc); } -Status CancelledError(absl::string_view message) { - return Status(absl::StatusCode::kCancelled, message); +Status CancelledError(absl::string_view message, absl::SourceLocation loc) { + return Status(absl::StatusCode::kCancelled, message, loc); } -Status DataLossError(absl::string_view message) { - return Status(absl::StatusCode::kDataLoss, message); +Status DataLossError(absl::string_view message, absl::SourceLocation loc) { + return Status(absl::StatusCode::kDataLoss, message, loc); } -Status DeadlineExceededError(absl::string_view message) { - return Status(absl::StatusCode::kDeadlineExceeded, message); +Status DeadlineExceededError(absl::string_view message, + absl::SourceLocation loc) { + return Status(absl::StatusCode::kDeadlineExceeded, message, loc); } -Status FailedPreconditionError(absl::string_view message) { - return Status(absl::StatusCode::kFailedPrecondition, message); +Status FailedPreconditionError(absl::string_view message, + absl::SourceLocation loc) { + return Status(absl::StatusCode::kFailedPrecondition, message, loc); } -Status InternalError(absl::string_view message) { - return Status(absl::StatusCode::kInternal, message); +Status InternalError(absl::string_view message, absl::SourceLocation loc) { + return Status(absl::StatusCode::kInternal, message, loc); } -Status InvalidArgumentError(absl::string_view message) { - return Status(absl::StatusCode::kInvalidArgument, message); +Status InvalidArgumentError(absl::string_view message, + absl::SourceLocation loc) { + return Status(absl::StatusCode::kInvalidArgument, message, loc); } -Status NotFoundError(absl::string_view message) { - return Status(absl::StatusCode::kNotFound, message); +Status NotFoundError(absl::string_view message, absl::SourceLocation loc) { + return Status(absl::StatusCode::kNotFound, message, loc); } -Status OutOfRangeError(absl::string_view message) { - return Status(absl::StatusCode::kOutOfRange, message); +Status OutOfRangeError(absl::string_view message, absl::SourceLocation loc) { + return Status(absl::StatusCode::kOutOfRange, message, loc); } -Status PermissionDeniedError(absl::string_view message) { - return Status(absl::StatusCode::kPermissionDenied, message); +Status PermissionDeniedError(absl::string_view message, + absl::SourceLocation loc) { + return Status(absl::StatusCode::kPermissionDenied, message, loc); } -Status ResourceExhaustedError(absl::string_view message) { - return Status(absl::StatusCode::kResourceExhausted, message); +Status ResourceExhaustedError(absl::string_view message, + absl::SourceLocation loc) { + return Status(absl::StatusCode::kResourceExhausted, message, loc); } -Status UnauthenticatedError(absl::string_view message) { - return Status(absl::StatusCode::kUnauthenticated, message); +Status UnauthenticatedError(absl::string_view message, + absl::SourceLocation loc) { + return Status(absl::StatusCode::kUnauthenticated, message, loc); } -Status UnavailableError(absl::string_view message) { - return Status(absl::StatusCode::kUnavailable, message); +Status UnavailableError(absl::string_view message, absl::SourceLocation loc) { + return Status(absl::StatusCode::kUnavailable, message, loc); } -Status UnimplementedError(absl::string_view message) { - return Status(absl::StatusCode::kUnimplemented, message); +Status UnimplementedError(absl::string_view message, absl::SourceLocation loc) { + return Status(absl::StatusCode::kUnimplemented, message, loc); } -Status UnknownError(absl::string_view message) { - return Status(absl::StatusCode::kUnknown, message); +Status UnknownError(absl::string_view message, absl::SourceLocation loc) { + return Status(absl::StatusCode::kUnknown, message, loc); } bool IsAborted(const Status& status) { @@ -404,9 +436,10 @@ } } // namespace -Status ErrnoToStatus(int error_number, absl::string_view message) { +Status ErrnoToStatus(int error_number, absl::string_view message, + absl::SourceLocation loc) { return Status(ErrnoToStatusCode(error_number), - MessageForErrnoToStatus(error_number, message)); + MessageForErrnoToStatus(error_number, message), loc); } const char* absl_nonnull StatusMessageAsCStr(const Status& status) {
diff --git a/absl/status/status.h b/absl/status/status.h index f81f912..62efd46 100644 --- a/absl/status/status.h +++ b/absl/status/status.h
@@ -68,6 +68,8 @@ #include "absl/strings/cord.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "absl/types/source_location.h" +#include "absl/types/span.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -306,6 +308,8 @@ kWithNoExtraData = 0, // ToString will contain the payloads. kWithPayload = 1 << 0, + // ToString will contain the source locations. + kWithSourceLocation = 1 << 1, // ToString will include all the extra data this Status has. kWithEverything = ~kWithNoExtraData, // Default mode used by ToString. Its exact value might change in the future. @@ -444,12 +448,28 @@ Status(); // Creates a status in the canonical error space with the specified - // `absl::StatusCode` and error message. If `code == absl::StatusCode::kOk`, // NOLINT + // `absl::StatusCode` and error message. If `code == absl::StatusCode::kOk`, // `msg` is ignored and an object identical to an OK status is constructed. // - // The `msg` string must be in UTF-8. The implementation may complain (e.g., // NOLINT + // The `msg` string must be in UTF-8. The implementation may complain (e.g., // by printing a warning) if it is not. - Status(absl::StatusCode code, absl::string_view msg); + // + // The `loc` is the SourceLocation of the callsite. It will be stored in the + // Status iff `code != absl::StatusCode::kOk` and `!msg.empty()`. + Status(absl::StatusCode code, absl::string_view msg, + absl::SourceLocation loc = SourceLocation::current()); + + // Create a status from a `base_status` and a `loc`. The `loc` will be + // appended to the location chain of the new status, iff the `base_status` is + // not ok and has non-empty msg. + Status(const Status& base_status, absl::SourceLocation loc) + : Status(base_status) { + AddSourceLocation(loc); + } + Status(Status&& base_status, absl::SourceLocation loc) + : Status(std::move(base_status)) { + AddSourceLocation(loc); + } Status(const Status&); Status& operator=(const Status& x); @@ -614,16 +634,88 @@ absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor) const; + absl::Span<const absl::SourceLocation> GetSourceLocations() const { + if (IsInlined(rep_)) return {}; + return RepToPointer(rep_)->GetSourceLocations(); + } + // Appends the `loc` to the current location chain inside the status, iff the + // status is non-ok and contains a non-empty message. + void AddSourceLocation( + absl::SourceLocation loc = absl::SourceLocation::current()) { + if (ok()) return; + rep_ = AddSourceLocationImpl(rep_, loc); + ABSL_ATTRIBUTE_UNUSED bool okay = ok(); + // This hint tells the optimizer that the status is still not ok after the + // AddSourceLocation() call. This is useful when passing a known !ok status + // to StatusOr. StatusOr checks for ok() on its constructor and this assume + // helps the optimizer remove that check. + ABSL_ASSUME(!okay); + } + + // Status::WithSourceLocation() + // + // Returns a copy of the current status, with `loc` appended to its location + // chain iff the status is non-ok and contains a non-empty message. + // + // Example: + // + // if (Status status = Foo(); !status.ok()) { + // return status.WithSourceLocation(); + // } + Status WithSourceLocation( + absl::SourceLocation loc = absl::SourceLocation::current()) const& { + return Status(*this, loc); + } + + // Status::WithSourceLocation() + // + // Appends the `loc` to the current location chain inside the status iff the + // status is non-ok and contains a non-empty message, and returns an rvalue + // reference to `*this`. + // + // Example: + // + // Status Finalize(...); + // + // Status DoSomething(...) { + // ... + // return Finalize().WithSourceLocation(); + // } + ABSL_MUST_USE_RESULT Status&& WithSourceLocation( + absl::SourceLocation loc = absl::SourceLocation::current()) && { + AddSourceLocation(loc); + return std::move(*this); + } + private: friend Status CancelledError(); +#ifndef SWIG + // Returns a `Status` object which is not `ok()` but + // `code() == absl::StatusCode::kOk`. This is necessary to be compatible with + // `Status` objects created with an error code in a custom `ErrorSpace` that + // is mapped to the canonical code `absl::StatusCode::kOk`. + static Status MakeNonOkStatusWithOkCode(absl::string_view message); + + friend class absl::status_internal::StatusPrivateAccessor; +#endif // !SWIG + // Creates a status in the canonical error space with the specified // code, and an empty error message. explicit Status(absl::StatusCode code); + // Delegate factory in header that ensures CodeToInlinedRep is inlined + // where possible. + static uintptr_t MakeRep(uintptr_t inlined_rep, absl::string_view msg, + absl::SourceLocation loc); + // Underlying constructor for status from a rep_. explicit Status(uintptr_t rep) : rep_(rep) {} + // An out-of-line AddSourceLocation that mutates rep directly. + static uintptr_t AddSourceLocationImpl(uintptr_t rep, + absl::SourceLocation loc); + static void Ref(uintptr_t rep); static void Unref(uintptr_t rep); @@ -741,22 +833,44 @@ // These convenience functions create an `absl::Status` object with an error // code as indicated by the associated function name, using the error message // passed in `message`. -Status AbortedError(absl::string_view message); -Status AlreadyExistsError(absl::string_view message); -Status CancelledError(absl::string_view message); -Status DataLossError(absl::string_view message); -Status DeadlineExceededError(absl::string_view message); -Status FailedPreconditionError(absl::string_view message); -Status InternalError(absl::string_view message); -Status InvalidArgumentError(absl::string_view message); -Status NotFoundError(absl::string_view message); -Status OutOfRangeError(absl::string_view message); -Status PermissionDeniedError(absl::string_view message); -Status ResourceExhaustedError(absl::string_view message); -Status UnauthenticatedError(absl::string_view message); -Status UnavailableError(absl::string_view message); -Status UnimplementedError(absl::string_view message); -Status UnknownError(absl::string_view message); +Status AbortedError(absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status AlreadyExistsError(absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status CancelledError(absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status DataLossError(absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status DeadlineExceededError( + absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status FailedPreconditionError( + absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status InternalError(absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status InvalidArgumentError( + absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status NotFoundError(absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status OutOfRangeError(absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status PermissionDeniedError( + absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status ResourceExhaustedError( + absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status UnauthenticatedError( + absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status UnavailableError(absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status UnimplementedError(absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); +Status UnknownError(absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); // ErrnoToStatusCode() // @@ -769,7 +883,8 @@ // // Convenience function that creates a `absl::Status` using an `error_number`, // which should be an `errno` value. -Status ErrnoToStatus(int error_number, absl::string_view message); +Status ErrnoToStatus(int error_number, absl::string_view message, + absl::SourceLocation loc = SourceLocation::current()); //------------------------------------------------------------------------------ // Implementation details follow @@ -779,6 +894,10 @@ inline Status::Status(absl::StatusCode code) : Status(CodeToInlinedRep(code)) {} +inline Status::Status(absl::StatusCode code, absl::string_view msg, + absl::SourceLocation loc) + : Status(MakeRep(CodeToInlinedRep(code), msg, loc)) {} + inline Status::Status(const Status& x) : Status(x.rep_) { Ref(rep_); } inline Status& Status::operator=(const Status& x) {
diff --git a/absl/status/status_benchmark.cc b/absl/status/status_benchmark.cc index a9146fb..539b783 100644 --- a/absl/status/status_benchmark.cc +++ b/absl/status/status_benchmark.cc
@@ -13,7 +13,9 @@ // limitations under the License. #include <utility> + #include "absl/status/status.h" +#include "absl/types/source_location.h" #include "benchmark/benchmark.h" namespace { @@ -34,4 +36,14 @@ } BENCHMARK(BM_CreateBad); +void BM_AppendSourceLocation(benchmark::State& state) { + for (auto _ : state) { + absl::Status s(absl::StatusCode::kInvalidArgument, "message"); + benchmark::DoNotOptimize(s); + absl::Status s2(std::move(s), absl::SourceLocation::current()); + benchmark::DoNotOptimize(s2); + } +} +BENCHMARK(BM_AppendSourceLocation); + } // namespace
diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc index f6ac0c0..ddf0be0 100644 --- a/absl/status/status_test.cc +++ b/absl/status/status_test.cc
@@ -20,12 +20,15 @@ #include <cstddef> #include <sstream> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/strings/cord.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "absl/types/source_location.h" namespace { @@ -46,9 +49,7 @@ // its creator, and its classifier. struct ErrorTest { absl::StatusCode code; - using Creator = absl::Status (*)( - absl::string_view - ); + using Creator = absl::Status (*)(absl::string_view, absl::SourceLocation); using Classifier = bool (*)(const absl::Status&); Creator creator; Classifier classifier; @@ -90,9 +91,8 @@ // expected error code and message. std::string message = absl::StrCat("error code ", test.code, " test message"); - absl::Status status = test.creator( - message - ); + absl::Status status = + test.creator(message, absl::SourceLocation::current()); EXPECT_EQ(test.code, status.code()); EXPECT_EQ(message, status.message()); @@ -342,32 +342,45 @@ absl::Status status(absl::StatusCode::kInternal, "fail"); { std::stringstream stream; stream << status; - EXPECT_EQ("INTERNAL: fail", stream.str()); + EXPECT_THAT(stream.str(), + AllOf(HasSubstr("INTERNAL: fail"), + HasSubstr("status_test.cc:"))); } status.SetPayload("foo", absl::Cord("bar")); { std::stringstream stream; stream << status; - EXPECT_EQ("INTERNAL: fail [foo='bar']", stream.str()); + EXPECT_THAT(stream.str(), + AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), + HasSubstr("status_test.cc:"))); } status.SetPayload("bar", absl::Cord("\377")); { std::stringstream stream; stream << status; EXPECT_THAT(stream.str(), AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), - HasSubstr("[bar='\\xff']"))); + HasSubstr("[bar='\\xff']"), + HasSubstr("status_test.cc:"))); } } TEST(Status, AbslStringify) { absl::Status status(absl::StatusCode::kInternal, "fail"); - EXPECT_EQ("INTERNAL: fail", absl::StrCat(status)); - EXPECT_EQ("INTERNAL: fail", absl::StrFormat("%v", status)); + EXPECT_THAT(absl::StrCat(status), + AllOf(HasSubstr("INTERNAL: fail"), + HasSubstr("status_test.cc:"))); + EXPECT_THAT(absl::StrFormat("%v", status), + AllOf(HasSubstr("INTERNAL: fail"), + HasSubstr("status_test.cc:"))); + EXPECT_EQ(absl::StrCat(status), absl::StrFormat("%v", status)); status.SetPayload("foo", absl::Cord("bar")); - EXPECT_EQ("INTERNAL: fail [foo='bar']", absl::StrCat(status)); + EXPECT_THAT(absl::StrCat(status), + AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), + HasSubstr("status_test.cc:"))); status.SetPayload("bar", absl::Cord("\377")); EXPECT_THAT(absl::StrCat(status), AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), - HasSubstr("[bar='\\xff']"))); + HasSubstr("[bar='\\xff']"), + HasSubstr("status_test.cc:"))); } TEST(Status, OstreamEqStringify) { @@ -577,4 +590,421 @@ EXPECT_EQ(status.message(), "Cannot open 'path': No such file or directory"); } +#ifdef ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE +#define GET_SOURCE_LOCATION(offset) __builtin_LINE() - offset +#else +#define GET_SOURCE_LOCATION(offset) 1 +#endif + +void CheckSourceLocation( + const absl::Status& status, std::vector<int> lines = {}, + absl::SourceLocation loc = absl::SourceLocation::current()) { + ASSERT_EQ(status.GetSourceLocations().size(), lines.size()) + << "Size check failed at " << loc.line(); + for (size_t i = 0; i < lines.size(); ++i) { + EXPECT_EQ(absl::string_view(status.GetSourceLocations()[i].file_name()), + absl::string_view(loc.file_name())) + << "File name check failed at " << loc.line(); + EXPECT_EQ(status.GetSourceLocations()[i].line(), lines[i]) + << "Line check failed at " << loc.line(); + } +} + +TEST(Status, ConstructorCheckSourceLocation) { + { + const absl::Status a; + const absl::Status b = a; + for (const absl::Status& status : {a, b}) { + EXPECT_TRUE(status.ok()); + EXPECT_EQ(absl::StatusCode::kOk, status.code()); + CheckSourceLocation(status); + } + } + { + const absl::Status a(absl::StatusCode::kInternal, "message", + absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + const absl::Status b = a; + for (const absl::Status& status : {a, b}) { + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status, {line}); + } + } + { + const absl::Status a(absl::StatusCode::kInternal, "message", + absl::SourceLocation()); + const absl::Status b = a; + for (const absl::Status& status : {a, b}) { + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + } + { + const absl::Status a(absl::StatusCode::kInternal, "", + absl::SourceLocation::current()); + const absl::Status b = a; + for (const absl::Status& status : {a, b}) { + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + } + { + const absl::Status a(absl::StatusCode::kInternal, "", + absl::SourceLocation()); + const absl::Status b = a; + for (const absl::Status& status : {a, b}) { + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + } +} + +TEST(Status, SourceLocationConstructor) { + { + // OK status doesn't save source locations. + const absl::Status original; + const absl::Status status(original, absl::SourceLocation()); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(absl::StatusCode::kOk, status.code()); + CheckSourceLocation(status); + } + { + // OK status doesn't save source locations. + const absl::Status original; + const absl::Status status(original, absl::SourceLocation::current()); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(absl::StatusCode::kOk, status.code()); + CheckSourceLocation(status); + } + { + // Non-ok Status with non-empty msg can save source locations with + // non-nullptr filename. + const absl::Status original(absl::StatusCode::kInternal, "message", + absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + // Default absl::SourceLocation cannot be saved into the chain. + const absl::Status status(original, absl::SourceLocation()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status, {line}); + } + { + const absl::Status original(absl::StatusCode::kInternal, "message", + absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + + const absl::Status status(original, absl::SourceLocation::current()); + int line2 = GET_SOURCE_LOCATION(1); + + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status, {line, line2}); + } + { + // Non-OK status with empty msg doesn't save source locations. + const absl::Status original(absl::StatusCode::kInternal, "", + absl::SourceLocation::current()); + const absl::Status status(original, absl::SourceLocation()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + { + // Non-OK status with empty msg doesn't save source locations. + const absl::Status original(absl::StatusCode::kInternal, "", + absl::SourceLocation::current()); + const absl::Status status(original, absl::SourceLocation::current()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + { + // Non-OK status with empty msg doesn't save source locations from default + // constructor. + const absl::Status original(absl::StatusCode::kInternal, "", + absl::SourceLocation()); + const absl::Status status(original, absl::SourceLocation()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + { + // Non-OK status with empty msg doesn't save source locations. + const absl::Status original(absl::StatusCode::kInternal, "", + absl::SourceLocation()); + const absl::Status status(original, absl::SourceLocation::current()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + { + const absl::Status original(absl::StatusCode::kInternal, "message", + absl::SourceLocation()); + const absl::Status status(original, absl::SourceLocation()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + { + const absl::Status original(absl::StatusCode::kInternal, "message", + absl::SourceLocation()); + const absl::Status status(original, absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status, {line}); + } +} + +TEST(Status, SourceLocationWithMoveConstructor) { + { + // OK status doesn't save source locations. + absl::Status original; + const absl::Status status(std::move(original), absl::SourceLocation()); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(absl::StatusCode::kOk, status.code()); + CheckSourceLocation(status); + } + { + // OK status doesn't save source locations. + absl::Status original; + const absl::Status status(std::move(original), + absl::SourceLocation::current()); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(absl::StatusCode::kOk, status.code()); + CheckSourceLocation(status); + } + { + // Non-ok Status with non-empty msg can save source locations with + // non-nullptr filename. + absl::Status original(absl::StatusCode::kInternal, "message", + absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + // Default absl::SourceLocation cannot be saved into the chain. + const absl::Status status(std::move(original), absl::SourceLocation()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status, {line}); + } + { + absl::Status original(absl::StatusCode::kInternal, "message", + absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + + const absl::Status status(std::move(original), + absl::SourceLocation::current()); + int line2 = GET_SOURCE_LOCATION(1); + + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status, {line, line2}); + } + { + // Non-OK status with empty msg doesn't save source locations. + absl::Status original(absl::StatusCode::kInternal, "", + absl::SourceLocation::current()); + const absl::Status status(std::move(original), absl::SourceLocation()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + { + // Non-OK status with empty msg doesn't save source locations. + absl::Status original(absl::StatusCode::kInternal, "", + absl::SourceLocation::current()); + const absl::Status status(std::move(original), + absl::SourceLocation::current()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + { + // Non-OK status with empty msg doesn't save source locations from default + // constructor. + absl::Status original(absl::StatusCode::kInternal, "", + absl::SourceLocation()); + const absl::Status status(std::move(original), absl::SourceLocation()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + { + // Non-OK status with empty msg doesn't save source locations. + absl::Status original(absl::StatusCode::kInternal, "", + absl::SourceLocation()); + const absl::Status status(std::move(original), + absl::SourceLocation::current()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + { + absl::Status original(absl::StatusCode::kInternal, "message", + absl::SourceLocation()); + const absl::Status status(std::move(original), absl::SourceLocation()); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status); + } + { + absl::Status original(absl::StatusCode::kInternal, "message", + absl::SourceLocation()); + const absl::Status status(std::move(original), + absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + CheckSourceLocation(status, {line}); + } +} + +TEST(Status, AddSourceLocation) { + int max_iter = 10; + { + // Status that ignores source location. + absl::Status status_ignores_source_location[] = { + absl::Status(), + absl::Status(absl::StatusCode::kInternal, "")}; + for (absl::Status& s : status_ignores_source_location) { + for (int i = 0; i < max_iter; ++i) { + s.AddSourceLocation(absl::SourceLocation::current()); + s.AddSourceLocation(absl::SourceLocation()); + } + CheckSourceLocation(s); + } + } + { + // Default SourceLocation is not added. + absl::Status status(absl::StatusCode::kInternal, "foo", + absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + for (int i = 0; i < max_iter; ++i) { + status.AddSourceLocation(absl::SourceLocation()); + } + CheckSourceLocation(status, {line}); + } + { + // Default SourceLocation is not added. + absl::Status status(absl::StatusCode::kInternal, "foo", + absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + std::vector<int> lines = {line}; + lines.reserve(1 + max_iter); + for (int i = 0; i < max_iter; ++i) { + status.AddSourceLocation(absl::SourceLocation::current()); + lines.push_back(GET_SOURCE_LOCATION(1)); + } + CheckSourceLocation(status, lines); + } +} + +TEST(Status, WithSourceLocationCopy) { + absl::Status original(absl::StatusCode::kInternal, "message", + absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + + const absl::Status status = + original.WithSourceLocation(absl::SourceLocation::current()); + int line2 = GET_SOURCE_LOCATION(1); + + CheckSourceLocation(original, {line}); + CheckSourceLocation(status, {line, line2}); + EXPECT_EQ(original, status); +} + +absl::Status&& IsRvalueStatus(absl::Status&& s) { return std::move(s); } + +TEST(Status, WithSourceLocationMove) { + absl::Status original(absl::StatusCode::kInternal, "message", + absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + + const absl::Status status = IsRvalueStatus( + std::move(original).WithSourceLocation(absl::SourceLocation::current())); + int line2 = GET_SOURCE_LOCATION(1); + + CheckSourceLocation(status, {line, line2}); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); +} + +TEST(Status, CopyOnWriteSourceLocations) { + absl::Status source(absl::StatusCode::kInvalidArgument, "fail", + absl::SourceLocation::current()); + EXPECT_EQ(source.GetSourceLocations().size(), 1); + absl::Status copy = source; + EXPECT_EQ(copy.GetSourceLocations().size(), 1); + copy.AddSourceLocation(absl::SourceLocation::current()); // Copy rep. + EXPECT_EQ(copy.GetSourceLocations().size(), 2); + EXPECT_EQ(source.GetSourceLocations().size(), 1); +} + +TEST(Status, SourceLocationToStringMode) { + absl::Status s(absl::StatusCode::kInternal, "fail", + absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + std::string source_location_string = "\n=== Source Location Trace: ==="; + std::string source_location_stack = absl::StrCat( + absl::SourceLocation::current().file_name(), ":", line, "\n"); + + s.SetPayload("foo", absl::Cord("bar")); + + EXPECT_EQ("INTERNAL: fail", + s.ToString(absl::StatusToStringMode::kWithNoExtraData)); + + EXPECT_EQ("INTERNAL: fail", + s.ToString(~absl::StatusToStringMode::kWithSourceLocation & + ~absl::StatusToStringMode::kWithPayload)); + EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithSourceLocation | + absl::StatusToStringMode::kWithPayload), + AllOf(HasSubstr("INTERNAL: fail [foo='bar']"), + HasSubstr(source_location_string), + HasSubstr(source_location_stack))); + + s.SetPayload("bar", absl::Cord("\377")); + + EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithEverything), + AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), + HasSubstr("[bar='\\xff']"), + HasSubstr(source_location_string), + HasSubstr(source_location_stack))); + EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithPayload | + absl::StatusToStringMode::kWithSourceLocation), + AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), + HasSubstr("[bar='\\xff']"), + HasSubstr(source_location_string), + HasSubstr(source_location_stack))); + EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithSourceLocation), + AllOf(HasSubstr("INTERNAL: fail"), Not(HasSubstr("[foo='bar']")), + Not(HasSubstr("[bar='\\xff']")), + HasSubstr(source_location_string), + HasSubstr(source_location_stack))); + EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithPayload), + AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), + HasSubstr("[bar='\\xff']"), + Not(HasSubstr(source_location_string)), + Not(HasSubstr(source_location_stack)))); +} + +TEST(Status, StackTracePayloadOverflow) { + // Stack must have the same layout as status_internal::StackTracePayload. + struct Stack { + size_t size; + void* frames[20]; + } stack; + stack.size = 200; // Overflows frames. + + absl::Status status = absl::CancelledError(); + status.SetPayload("AbslStatusStackTracePayload", + absl::Cord(absl::string_view( + reinterpret_cast<const char*>(&stack), sizeof(stack)))); + + // An unchecked overflow should be detected by ASAN/MSAN on the next line. + static_cast<void>(status.ToString(absl::StatusToStringMode::kWithEverything)); +} + } // namespace
diff --git a/absl/status/statusor.h b/absl/status/statusor.h index 2427d24..0af1d75 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h
@@ -54,6 +54,8 @@ #include "absl/strings/has_absl_stringify.h" #include "absl/strings/has_ostream_operator.h" #include "absl/strings/str_format.h" +#include "absl/types/source_location.h" +#include "absl/types/span.h" #include "absl/types/variant.h" #include "absl/utility/utility.h" @@ -245,50 +247,50 @@ // is explicit if and only if the corresponding construction of `T` from `U` // is explicit. (This constructor inherits its explicitness from the // underlying constructor.) - template <typename U, absl::enable_if_t< + template <typename U, std::enable_if_t< internal_statusor::IsConstructionFromStatusOrValid< false, T, U, false, const U&>::value, int> = 0> StatusOr(const StatusOr<U>& other) // NOLINT : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {} - template <typename U, absl::enable_if_t< + template <typename U, std::enable_if_t< internal_statusor::IsConstructionFromStatusOrValid< false, T, U, true, const U&>::value, int> = 0> StatusOr(const StatusOr<U>& other ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {} - template <typename U, absl::enable_if_t< + template <typename U, std::enable_if_t< internal_statusor::IsConstructionFromStatusOrValid< true, T, U, false, const U&>::value, int> = 0> explicit StatusOr(const StatusOr<U>& other) : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {} - template <typename U, absl::enable_if_t< + template <typename U, std::enable_if_t< internal_statusor::IsConstructionFromStatusOrValid< true, T, U, true, const U&>::value, int> = 0> explicit StatusOr(const StatusOr<U>& other ABSL_ATTRIBUTE_LIFETIME_BOUND) : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {} - template <typename U, absl::enable_if_t< + template <typename U, std::enable_if_t< internal_statusor::IsConstructionFromStatusOrValid< false, T, U, false, U&&>::value, int> = 0> StatusOr(StatusOr<U>&& other) // NOLINT : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {} - template <typename U, absl::enable_if_t< + template <typename U, std::enable_if_t< internal_statusor::IsConstructionFromStatusOrValid< false, T, U, true, U&&>::value, int> = 0> StatusOr(StatusOr<U>&& other ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {} - template <typename U, absl::enable_if_t< + template <typename U, std::enable_if_t< internal_statusor::IsConstructionFromStatusOrValid< true, T, U, false, U&&>::value, int> = 0> explicit StatusOr(StatusOr<U>&& other) : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {} - template <typename U, absl::enable_if_t< + template <typename U, std::enable_if_t< internal_statusor::IsConstructionFromStatusOrValid< true, T, U, true, U&&>::value, int> = 0> @@ -315,7 +317,7 @@ // assignable from `absl::StatusOr<U>` and `StatusOr<T>` cannot be directly // assigned from `StatusOr<U>`. template <typename U, - absl::enable_if_t<internal_statusor::IsStatusOrAssignmentValid< + std::enable_if_t<internal_statusor::IsStatusOrAssignmentValid< T, const U&, false>::value, int> = 0> StatusOr& operator=(const StatusOr<U>& other) { @@ -323,7 +325,7 @@ return *this; } template <typename U, - absl::enable_if_t<internal_statusor::IsStatusOrAssignmentValid< + std::enable_if_t<internal_statusor::IsStatusOrAssignmentValid< T, const U&, true>::value, int> = 0> StatusOr& operator=(const StatusOr<U>& other ABSL_ATTRIBUTE_LIFETIME_BOUND) { @@ -331,7 +333,7 @@ return *this; } template <typename U, - absl::enable_if_t<internal_statusor::IsStatusOrAssignmentValid< + std::enable_if_t<internal_statusor::IsStatusOrAssignmentValid< T, U&&, false>::value, int> = 0> StatusOr& operator=(StatusOr<U>&& other) { @@ -339,7 +341,7 @@ return *this; } template <typename U, - absl::enable_if_t<internal_statusor::IsStatusOrAssignmentValid< + std::enable_if_t<internal_statusor::IsStatusOrAssignmentValid< T, U&&, true>::value, int> = 0> StatusOr& operator=(StatusOr<U>&& other ABSL_ATTRIBUTE_LIFETIME_BOUND) { @@ -359,18 +361,18 @@ // In optimized builds, passing absl::OkStatus() here will have the effect // of passing absl::StatusCode::kInternal as a fallback. template <typename U = absl::Status, - absl::enable_if_t<internal_statusor::IsConstructionFromStatusValid< + std::enable_if_t<internal_statusor::IsConstructionFromStatusValid< false, T, U>::value, int> = 0> StatusOr(U&& v) : Base(std::forward<U>(v)) {} template <typename U = absl::Status, - absl::enable_if_t<internal_statusor::IsConstructionFromStatusValid< + std::enable_if_t<internal_statusor::IsConstructionFromStatusValid< true, T, U>::value, int> = 0> explicit StatusOr(U&& v) : Base(std::forward<U>(v)) {} template <typename U = absl::Status, - absl::enable_if_t<internal_statusor::IsConstructionFromStatusValid< + std::enable_if_t<internal_statusor::IsConstructionFromStatusValid< false, T, U>::value, int> = 0> StatusOr& operator=(U&& v) { @@ -427,26 +429,26 @@ // ambiguity, this constructor is disabled if `U` is a `StatusOr<J>`, where // `J` is convertible to `T`. template <typename U = T, - absl::enable_if_t<internal_statusor::IsConstructionValid< + std::enable_if_t<internal_statusor::IsConstructionValid< false, T, U, false>::value, int> = 0> StatusOr(U&& u) // NOLINT : StatusOr(absl::in_place, std::forward<U>(u)) {} template <typename U = T, - absl::enable_if_t<internal_statusor::IsConstructionValid< + std::enable_if_t<internal_statusor::IsConstructionValid< false, T, U, true>::value, int> = 0> StatusOr(U&& u ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT : StatusOr(absl::in_place, std::forward<U>(u)) {} template <typename U = T, - absl::enable_if_t<internal_statusor::IsConstructionValid< + std::enable_if_t<internal_statusor::IsConstructionValid< true, T, U, false>::value, int> = 0> explicit StatusOr(U&& u) // NOLINT : StatusOr(absl::in_place, std::forward<U>(u)) {} template <typename U = T, - absl::enable_if_t< + std::enable_if_t< internal_statusor::IsConstructionValid<true, T, U, true>::value, int> = 0> explicit StatusOr(U&& u ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT @@ -476,6 +478,36 @@ ABSL_MUST_USE_RESULT const Status& status() const&; Status status() &&; + absl::Span<const absl::SourceLocation> GetSourceLocations() const { + return this->status_.GetSourceLocations(); + } + // Appends the `loc` to the current location chain inside the status, iff the + // status-or is non-ok and contains a non-empty message. + void AddSourceLocation( + absl::SourceLocation loc = absl::SourceLocation::current()) { + this->status_.AddSourceLocation(loc); + } + + // StatusOr<T>::WithSourceLocation() + // + // Appends the `loc` to the current location chain inside the status iff the + // status-or is non-ok and contains a non-empty message, and returns an rvalue + // reference to `*this`. + // + // Example: + // + // StatusOr<int> Finalize(...); + // + // StatusOr<int> DoSomething(...) { + // ... + // return Finalize().WithSourceLocation(); + // } + ABSL_MUST_USE_RESULT StatusOr<T>&& WithSourceLocation( + absl::SourceLocation loc = absl::SourceLocation::current()) && { + AddSourceLocation(loc); + return std::move(*this); + } + // StatusOr<T>::value() // // Returns a reference to the held value if `this->ok()`. Otherwise, throws @@ -592,7 +624,7 @@ template < typename U, typename... Args, - absl::enable_if_t< + std::enable_if_t< std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, int> = 0> T& emplace(std::initializer_list<U> ilist,
diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc index 82cb930..bd2c10e 100644 --- a/absl/status/statusor_test.cc +++ b/absl/status/statusor_test.cc
@@ -37,6 +37,7 @@ #include "absl/status/status_matchers.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "absl/types/source_location.h" #include "absl/utility/utility.h" namespace { @@ -1800,6 +1801,102 @@ EXPECT_THAT(absl::StrCat(print_me), error_matcher); } +#ifdef ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE +#define GET_SOURCE_LOCATION(offset) __builtin_LINE() - offset +#else +#define GET_SOURCE_LOCATION(offset) 1 +#endif + +template <typename T> +void CheckSourceLocation( + const absl::StatusOr<T>& status_or, std::vector<int> lines = {}, + absl::SourceLocation loc = absl::SourceLocation::current()) { + ASSERT_EQ(status_or.GetSourceLocations().size(), lines.size()) + << "Size check failed at " << loc.line(); + for (size_t i = 0; i < lines.size(); ++i) { + EXPECT_EQ(absl::string_view(status_or.GetSourceLocations()[i].file_name()), + absl::string_view(loc.file_name())) + << "File name check failed at " << loc.line(); + EXPECT_EQ(status_or.GetSourceLocations()[i].line(), lines[i]) + << "Line check failed at " << loc.line(); + } +} + +TEST(StatusOr, AddSourceLocation) { + constexpr int kMaxIter = 10; + { + // Status that ignores source location. + absl::StatusOr<int> status_ignores_source_location[] = { + 123, absl::Status(absl::StatusCode::kInternal, "")}; + for (absl::StatusOr<int>& s : status_ignores_source_location) { + for (int i = 0; i < kMaxIter; ++i) { + s.AddSourceLocation(absl::SourceLocation::current()); + s.AddSourceLocation(absl::SourceLocation()); + } + CheckSourceLocation(s); + } + } + { + // Default SourceLocation is not added. + absl::StatusOr<int> status = absl::Status( + absl::StatusCode::kInternal, "foo", absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + for (int i = 0; i < kMaxIter; ++i) { + status.AddSourceLocation(absl::SourceLocation()); + } + CheckSourceLocation(status, {line}); + } + { + // Default SourceLocation is not added. + absl::StatusOr<int> status = absl::Status( + absl::StatusCode::kInternal, "foo", absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + std::vector<int> lines = {line}; + lines.reserve(1 + kMaxIter); + for (int i = 0; i < kMaxIter; ++i) { + status.AddSourceLocation(absl::SourceLocation::current()); + lines.push_back(GET_SOURCE_LOCATION(1)); + } + CheckSourceLocation(status, lines); + } +} + +absl::StatusOr<int>&& IsRvalueStatus(absl::StatusOr<int>&& s) { + return std::move(s); +} + +TEST(StatusOr, WithSourceLocationMove) { + absl::StatusOr<int> original = absl::Status( + absl::StatusCode::kInternal, "message", absl::SourceLocation::current()); + int line = GET_SOURCE_LOCATION(1); + + const absl::StatusOr<int> status_or = IsRvalueStatus( + std::move(original).WithSourceLocation(absl::SourceLocation::current())); + int line2 = GET_SOURCE_LOCATION(1); + + CheckSourceLocation(status_or, {line, line2}); + EXPECT_FALSE(status_or.ok()); +} + +TEST(StatusOr, WithSourceLocationReturn) { + absl::SourceLocation loc1 = absl::SourceLocation::current(); + int line1 = GET_SOURCE_LOCATION(1); + absl::SourceLocation loc2 = absl::SourceLocation::current(); + int line2 = GET_SOURCE_LOCATION(1); + + const auto return_error = [&loc1]() -> absl::StatusOr<int> { + return absl::InvalidArgumentError("I am error", loc1); + }; + const auto return_error_with_source_location = + [&return_error, &loc2]() -> absl::StatusOr<int> { + return return_error().WithSourceLocation(loc2); + }; + + absl::StatusOr<int> status_or = return_error_with_source_location(); + CheckSourceLocation(status_or, {line1, line2}); + EXPECT_FALSE(status_or.ok()); +} + TEST(StatusOr, SupportsReferenceTypes) { int i = 1; absl::StatusOr<int&> s = i;
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 5f42894..79b6672 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel
@@ -114,6 +114,7 @@ "//absl/meta:type_traits", "//absl/numeric:bits", "//absl/numeric:int128", + "//absl/types:source_location", ], ) @@ -400,7 +401,6 @@ visibility = ["//visibility:private"], deps = [ ":strings", - "//absl/meta:type_traits", "@googletest//:gtest", "@googletest//:gtest_main", ], @@ -423,6 +423,36 @@ ], ) +cc_library( + name = "stringify_stream", + hdrs = ["internal/stringify_stream.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:friends", + "//visibility:private", + ], + deps = [ + ":string_view", + "//absl/base:config", + ], +) + +cc_test( + name = "stringify_stream_test", + srcs = ["internal/stringify_stream_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":string_view", + ":stringify_stream", + "//absl/base:config", + "//absl/strings:str_format", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + cc_binary( name = "charset_benchmark", testonly = True, @@ -646,6 +676,7 @@ "//absl/base:endian", "//absl/base:nullability", "//absl/base:raw_logging_internal", + "//absl/cleanup", "//absl/container:inlined_vector", "//absl/crc:crc32c", "//absl/crc:crc_cord_state", @@ -1084,6 +1115,7 @@ "//absl/container:btree", "//absl/container:flat_hash_map", "//absl/container:node_hash_map", + "//absl/hash", "@googletest//:gtest", "@googletest//:gtest_main", ],
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 0e0adfa..9f7f9ef 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt
@@ -86,6 +86,8 @@ absl::memory absl::nullability absl::raw_logging_internal + absl::source_location + absl::strings absl::throw_delegate absl::type_traits PUBLIC @@ -116,6 +118,32 @@ PUBLIC ) +absl_cc_library( + NAME + stringify_stream + HDRS + "internal/stringify_stream.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::string_view + absl::config +) + +absl_cc_test( + NAME + stringify_stream_test + SRCS + "internal/stringify_stream_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::string_view + absl::stringify_stream + absl::str_format + GTest::gmock_main +) + # Internal-only target, do not depend on directly. absl_cc_library( NAME @@ -311,7 +339,6 @@ ${ABSL_TEST_COPTS} DEPS absl::strings - absl::type_traits GTest::gmock_main ) @@ -369,6 +396,7 @@ absl::dynamic_annotations absl::btree absl::flat_hash_map + absl::hash absl::node_hash_map GTest::gmock_main )
diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 3278296..c2f1ec5 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h
@@ -174,7 +174,7 @@ private: template <typename T> using EnableIfString = - absl::enable_if_t<std::is_same<T, std::string>::value, int>; + std::enable_if_t<std::is_same<T, std::string>::value, int>; public: // Cord::Cord() Constructors. @@ -1137,7 +1137,7 @@ CordRep* absl_nonnull NewExternalRep(absl::string_view data, Releaser&& releaser) { assert(!data.empty()); - using ReleaserType = absl::decay_t<Releaser>; + using ReleaserType = std::decay_t<Releaser>; CordRepExternal* rep = new CordRepExternalImpl<ReleaserType>( std::forward<Releaser>(releaser), 0); InitializeCordRepExternal(data, rep); @@ -1162,7 +1162,7 @@ data, std::forward<Releaser>(releaser)), Cord::MethodIdentifier::kMakeCordFromExternal); } else { - using ReleaserType = absl::decay_t<Releaser>; + using ReleaserType = std::decay_t<Releaser>; cord_internal::InvokeReleaser( cord_internal::Rank1{}, ReleaserType(std::forward<Releaser>(releaser)), data);
diff --git a/absl/strings/internal/generic_printer_internal.h b/absl/strings/internal/generic_printer_internal.h index 1a2d0bd..fc88b27 100644 --- a/absl/strings/internal/generic_printer_internal.h +++ b/absl/strings/internal/generic_printer_internal.h
@@ -112,6 +112,23 @@ // would be a fork. decltype(T().~ArenaSafeUniquePtr())> = true; +// `proto2::Arena::UniquePtr` is at least as safe as `std::unique_ptr`. +template <class T> +inline constexpr bool is_supported_ptr< + T, + // Check for `proto2::Arena::UniquePtr` without having to include its + // header here. This does match any type named `UniquePtr`, regardless + // of the scope it is defined in, but we try to restrict by probing some + // methods. + std::void_t<decltype( + // Check the class name using the destructor. + T().~UniquePtr(), + // Check some other members, to try to duck type into the right class. + T().get(), T().reset(), T().try_heap_release(), + T().GetOwningArena() + ->template MakeUnique<int>(nullptr) + .~UniquePtr())>> = true; + // Specialization for floats: print floating point types using their // max_digits10 precision. This ensures each distinct underlying values // can be represented uniquely, even though it's not (strictly speaking) @@ -220,6 +237,8 @@ if constexpr (is_any_string<T> || std::is_same_v<T, absl::string_view>) { // Specialization for strings: prints with plausible quoting and escaping. return PrintEscapedString(os, v); + } else if constexpr (is_supported_ptr<T>) { + return (PrintSmartPointerContents)(os, v); } else if constexpr (absl::HasAbslStringify<T>::value) { // If someone has specified `AbslStringify`, we should prefer that. return os << absl::StrCat(v); @@ -246,8 +265,6 @@ w)) {})) { // For std::variant, use `std::visit(v)` return (PrintVariant)(os, v); - } else if constexpr (is_supported_ptr<T>) { - return (PrintSmartPointerContents)(os, v); } else if constexpr (meta_internal::Requires<const T>( [&](auto&& w) -> decltype(w.ok(), w.status(), *w) { })) {
diff --git a/absl/strings/internal/stl_type_traits.h b/absl/strings/internal/stl_type_traits.h index e50468b..9c63726 100644 --- a/absl/strings/internal/stl_type_traits.h +++ b/absl/strings/internal/stl_type_traits.h
@@ -48,25 +48,25 @@ template <template <typename...> class T, typename... Args> struct IsSpecializationImpl<T<Args...>, T> : std::true_type {}; template <typename C, template <typename...> class T> -using IsSpecialization = IsSpecializationImpl<absl::decay_t<C>, T>; +using IsSpecialization = IsSpecializationImpl<std::decay_t<C>, T>; template <typename C> struct IsArrayImpl : std::false_type {}; template <template <typename, size_t> class A, typename T, size_t N> struct IsArrayImpl<A<T, N>> : std::is_same<A<T, N>, std::array<T, N>> {}; template <typename C> -using IsArray = IsArrayImpl<absl::decay_t<C>>; +using IsArray = IsArrayImpl<std::decay_t<C>>; template <typename C> struct IsBitsetImpl : std::false_type {}; template <template <size_t> class B, size_t N> struct IsBitsetImpl<B<N>> : std::is_same<B<N>, std::bitset<N>> {}; template <typename C> -using IsBitset = IsBitsetImpl<absl::decay_t<C>>; +using IsBitset = IsBitsetImpl<std::decay_t<C>>; template <typename C> struct IsSTLContainer - : absl::disjunction< + : std::disjunction< IsArray<C>, IsBitset<C>, IsSpecialization<C, std::deque>, IsSpecialization<C, std::forward_list>, IsSpecialization<C, std::list>, IsSpecialization<C, std::map>, @@ -123,7 +123,7 @@ typename C::hasher, typename C::key_equal, typename C::allocator_type>> {}; template <typename C, template <typename...> class T> -using IsBaseOfSpecialization = IsBaseOfSpecializationImpl<absl::decay_t<C>, T>; +using IsBaseOfSpecialization = IsBaseOfSpecializationImpl<std::decay_t<C>, T>; template <typename C> struct IsBaseOfArrayImpl : std::false_type {}; @@ -131,18 +131,18 @@ struct IsBaseOfArrayImpl<A<T, N>> : std::is_base_of<A<T, N>, std::array<T, N>> { }; template <typename C> -using IsBaseOfArray = IsBaseOfArrayImpl<absl::decay_t<C>>; +using IsBaseOfArray = IsBaseOfArrayImpl<std::decay_t<C>>; template <typename C> struct IsBaseOfBitsetImpl : std::false_type {}; template <template <size_t> class B, size_t N> struct IsBaseOfBitsetImpl<B<N>> : std::is_base_of<B<N>, std::bitset<N>> {}; template <typename C> -using IsBaseOfBitset = IsBaseOfBitsetImpl<absl::decay_t<C>>; +using IsBaseOfBitset = IsBaseOfBitsetImpl<std::decay_t<C>>; template <typename C> struct IsBaseOfSTLContainer - : absl::disjunction<IsBaseOfArray<C>, IsBaseOfBitset<C>, + : std::disjunction<IsBaseOfArray<C>, IsBaseOfBitset<C>, IsBaseOfSpecialization<C, std::deque>, IsBaseOfSpecialization<C, std::forward_list>, IsBaseOfSpecialization<C, std::list>, @@ -201,7 +201,7 @@ typename C::allocator_type>> {}; template <typename C, template <typename...> class T> using IsConvertibleToSpecialization = - IsConvertibleToSpecializationImpl<absl::decay_t<C>, T>; + IsConvertibleToSpecializationImpl<std::decay_t<C>, T>; template <typename C> struct IsConvertibleToArrayImpl : std::false_type {}; @@ -209,7 +209,7 @@ struct IsConvertibleToArrayImpl<A<T, N>> : std::is_convertible<A<T, N>, std::array<T, N>> {}; template <typename C> -using IsConvertibleToArray = IsConvertibleToArrayImpl<absl::decay_t<C>>; +using IsConvertibleToArray = IsConvertibleToArrayImpl<std::decay_t<C>>; template <typename C> struct IsConvertibleToBitsetImpl : std::false_type {}; @@ -217,11 +217,11 @@ struct IsConvertibleToBitsetImpl<B<N>> : std::is_convertible<B<N>, std::bitset<N>> {}; template <typename C> -using IsConvertibleToBitset = IsConvertibleToBitsetImpl<absl::decay_t<C>>; +using IsConvertibleToBitset = IsConvertibleToBitsetImpl<std::decay_t<C>>; template <typename C> struct IsConvertibleToSTLContainer - : absl::disjunction< + : std::disjunction< IsConvertibleToArray<C>, IsConvertibleToBitset<C>, IsConvertibleToSpecialization<C, std::deque>, IsConvertibleToSpecialization<C, std::forward_list>, @@ -238,7 +238,7 @@ template <typename C> struct IsStrictlyBaseOfAndConvertibleToSTLContainer - : absl::conjunction<absl::negation<IsSTLContainer<C>>, + : std::conjunction<std::negation<IsSTLContainer<C>>, IsBaseOfSTLContainer<C>, IsConvertibleToSTLContainer<C>> {};
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index 021013f..0fd3d99 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h
@@ -133,9 +133,9 @@ std::declval<const FormatConversionSpec&>(), std::declval<FormatSink*>())) { using FormatConversionSpecT = - absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>; + std::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>; using FormatSinkT = - absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; + std::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; auto fcs = conv.Wrap<FormatConversionSpecT>(); auto fs = sink->Wrap<FormatSinkT>(); return AbslFormatConvert(v, fcs, &fs); @@ -150,7 +150,7 @@ IntegralConvertResult> { if (conv.conversion_char() == FormatConversionCharInternal::v) { using FormatSinkT = - absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; + std::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; auto fs = sink->Wrap<FormatSinkT>(); AbslStringify(fs, v); return {true}; @@ -169,7 +169,7 @@ std::declval<FormatSink&>(), v))>::value, ArgConvertResult<FormatConversionCharSetInternal::v>> { using FormatSinkT = - absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; + std::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; auto fs = sink->Wrap<FormatSinkT>(); AbslStringify(fs, v); return {true}; @@ -345,7 +345,7 @@ // This function needs to be a template due to ambiguity regarding type // conversions. -template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0> +template <typename T, std::enable_if_t<std::is_same<T, bool>::value, int> = 0> IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { if (conv.conversion_char() == FormatConversionCharInternal::v) { @@ -381,7 +381,7 @@ static ArgConvertResult<FormatConversionCharSetInternal::n> ConvertHelper( const FormatCountCapture& v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { - const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v; + const std::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v; if (conv.conversion_char() != str_format_internal::FormatConversionCharInternal::n) {
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc index 89275d4..265c53d 100644 --- a/absl/strings/internal/str_format/float_conversion.cc +++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -25,6 +25,7 @@ #include <limits> #include <optional> #include <string> +#include <type_traits> #include "absl/base/attributes.h" #include "absl/base/config.h" @@ -100,7 +101,7 @@ // Requires: `0 <= carry <= 9` template <typename Int> inline char MultiplyBy10WithCarry(Int* v, char carry) { - using BiggerInt = absl::conditional_t<sizeof(Int) == 4, uint64_t, uint128>; + using BiggerInt = std::conditional_t<sizeof(Int) == 4, uint64_t, uint128>; BiggerInt tmp = 10 * static_cast<BiggerInt>(*v) + static_cast<BiggerInt>(carry); *v = static_cast<Int>(tmp); @@ -1184,8 +1185,8 @@ template <typename Float> struct Decomposed { using MantissaType = - absl::conditional_t<std::is_same<long double, Float>::value, uint128, - uint64_t>; + std::conditional_t<std::is_same<long double, Float>::value, uint128, + uint64_t>; static_assert(std::numeric_limits<Float>::digits <= sizeof(MantissaType) * 8, ""); MantissaType mantissa;
diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h index 31f1d65..9bda311 100644 --- a/absl/strings/internal/str_split_internal.h +++ b/absl/strings/internal/str_split_internal.h
@@ -224,7 +224,7 @@ template <typename C> struct SplitterIsConvertibleToImpl<C, true, true> - : absl::conjunction< + : std::conjunction< std::is_constructible<typename C::key_type, absl::string_view>, std::is_constructible<typename C::mapped_type, absl::string_view>> {}; @@ -494,7 +494,7 @@ // Inserts the key and an empty value into the map, returning an iterator to // the inserted item. We use emplace() if available, otherwise insert(). template <typename M> - static absl::enable_if_t<HasEmplace<M>::value, iterator> InsertOrEmplace( + static std::enable_if_t<HasEmplace<M>::value, iterator> InsertOrEmplace( M* m, absl::string_view key) { // Use piecewise_construct to support old versions of gcc in which pair // constructor can't otherwise construct string from string_view. @@ -502,7 +502,7 @@ std::tuple<>())); } template <typename M> - static absl::enable_if_t<!HasEmplace<M>::value, iterator> InsertOrEmplace( + static std::enable_if_t<!HasEmplace<M>::value, iterator> InsertOrEmplace( M* m, absl::string_view key) { return ToIter(m->insert(std::make_pair(First(key), Second("")))); }
diff --git a/absl/strings/internal/string_constant_test.cc b/absl/strings/internal/string_constant_test.cc index 392833c..c8b3591 100644 --- a/absl/strings/internal/string_constant_test.cc +++ b/absl/strings/internal/string_constant_test.cc
@@ -14,7 +14,8 @@ #include "absl/strings/internal/string_constant.h" -#include "absl/meta/type_traits.h" +#include <type_traits> + #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -34,10 +35,10 @@ EXPECT_TRUE(std::is_empty<T>::value); EXPECT_TRUE(std::is_trivial<T>::value); - EXPECT_TRUE(absl::is_trivially_default_constructible<T>::value); - EXPECT_TRUE(absl::is_trivially_copy_constructible<T>::value); - EXPECT_TRUE(absl::is_trivially_move_constructible<T>::value); - EXPECT_TRUE(absl::is_trivially_destructible<T>::value); + EXPECT_TRUE(std::is_trivially_default_constructible<T>::value); + EXPECT_TRUE(std::is_trivially_copy_constructible<T>::value); + EXPECT_TRUE(std::is_trivially_move_constructible<T>::value); + EXPECT_TRUE(std::is_trivially_destructible<T>::value); } TEST(StringConstant, MakeFromCallable) {
diff --git a/absl/strings/internal/stringify_sink.h b/absl/strings/internal/stringify_sink.h index fc3747b..6478ea5 100644 --- a/absl/strings/internal/stringify_sink.h +++ b/absl/strings/internal/stringify_sink.h
@@ -15,11 +15,14 @@ #ifndef ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_ #define ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_ +#include <array> #include <string> #include <type_traits> #include <utility> +#include "absl/strings/numbers.h" #include "absl/strings/string_view.h" +#include "absl/types/source_location.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -51,6 +54,15 @@ } // namespace strings_internal +template <typename Sink> +void AbslStringify(Sink& sink, SourceLocation l) { + sink.Append(l.file_name()); + sink.Append(":"); + std::array<char, numbers_internal::kFastToBufferSize> buffer; + numbers_internal::FastIntToBuffer(l.line(), buffer.data()); + sink.Append(buffer.data()); +} + ABSL_NAMESPACE_END } // namespace absl
diff --git a/absl/strings/internal/stringify_stream.h b/absl/strings/internal/stringify_stream.h new file mode 100644 index 0000000..6110b33 --- /dev/null +++ b/absl/strings/internal/stringify_stream.h
@@ -0,0 +1,119 @@ +// 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: stringify_stream.h +// ----------------------------------------------------------------------------- + +#ifndef ABSL_STRINGS_INTERNAL_STRINGIFY_STREAM_H_ +#define ABSL_STRINGS_INTERNAL_STRINGIFY_STREAM_H_ + +// StringifyStream is an adaptor for any std::ostream, that provides a +// stream insertion (<<) operator with the following behavior when inserting +// some value of type T: +// +// - If there is a suitable overload of operator<< already defined for T, it +// will be used. +// +// - If there is no operator<< overload, but there is an AbslStringify defined +// for T, it will be used as a fallback. +// +// - Otherwise it is a compilation error. +// +// For reference, AbslStringify typically has the form: +// +// struct Foo { +// template <typename Sink> +// friend void AbslStringify(Sink& sink, const Foo& foo) { ... } +// }; +// +// This permits the following usage, for example: +// +// StringifyStream(std::cout) << Foo(); +// + +#include <cstddef> +#include <ostream> +#include <string> +#include <type_traits> +#include <utility> + +#include "absl/base/config.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace strings_internal { + +class StringifyStream { + public: + // Constructor: adapts (but does not take ownership of) some underlying + // std::ostream instance. + explicit StringifyStream(std::ostream& os) : sink_{os} {} + + // Stream insertion: delegate to an overload of operator<< if defined for type + // T, otherwise fall back to AbslStringify for T. + template <typename T> + friend StringifyStream& operator<<(StringifyStream& stream, const T& t) { + if constexpr (HasStreamInsertion<T>::value) { + stream.sink_.os << t; + } else { + AbslStringify(stream.sink_, t); + } + return stream; + } + + // Rvalue-ref overload, required when the StringifyStream parameter hasn't + // been bound to a variable. + template <typename T> + friend StringifyStream& operator<<(StringifyStream&& stream, const T& t) { + return stream << t; + } + + // Overload for things like << std::endl which need an explicit type in order + // to resolve to the appropriate overload or template instantiation. + StringifyStream& operator<<(std::ostream& (*func)(std::ostream&)) { + sink_.os << func; + return *this; + } + + private: + // Abseil "stringify sink" concept (stringify_sink.h) + struct Sink { + std::ostream& os; + void Append(size_t count, char ch) { os << std::string(count, ch); } + void Append(absl::string_view v) { os << v; } + friend void AbslFormatFlush(Sink* sink, absl::string_view v) { + sink->Append(v); + } + } sink_; + + // SFINAE helper to identify types with a defined operator<< overload. + template <typename T, typename = void> + struct HasStreamInsertion : std::false_type {}; + + template <typename T> + struct HasStreamInsertion<T, + std::void_t<decltype(std::declval<std::ostream&>() + << std::declval<const T&>())>> + : std::true_type {}; +}; + +} // namespace strings_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_STRINGIFY_STREAM_H_
diff --git a/absl/strings/internal/stringify_stream_test.cc b/absl/strings/internal/stringify_stream_test.cc new file mode 100644 index 0000000..71a7691 --- /dev/null +++ b/absl/strings/internal/stringify_stream_test.cc
@@ -0,0 +1,111 @@ +// 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 "absl/strings/internal/stringify_stream.h" + +#include <cstddef> +#include <iomanip> +#include <ostream> +#include <sstream> + +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace strings_internal { +namespace { + +// Exercises the Append(size_t, char) overload +struct AppendNCharsTest { + size_t count; + char ch; + + template <typename Sink> + friend void AbslStringify(Sink& sink, const AppendNCharsTest& t) { + sink.Append(t.count, t.ch); + } +}; +TEST(StringifyStreamTest, AppendNChars) { + std::ostringstream os; + StringifyStream(os) << AppendNCharsTest{5, 'a'}; + EXPECT_EQ(os.str(), "aaaaa"); +} + +// Exercises the Append(absl::string_view) overload +struct AppendStringViewTest { + absl::string_view v; + + template <typename Sink> + friend void AbslStringify(Sink& sink, const AppendStringViewTest& t) { + sink.Append(t.v); + } +}; +TEST(StringifyStreamTest, AppendStringView) { + std::ostringstream os; + StringifyStream(os) << AppendStringViewTest{"abc"}; + EXPECT_EQ(os.str(), "abc"); +} + +// Exercises AbslFormatFlush(OStringStreamSink*, absl::string_view v) +struct AbslFormatFlushTest { + absl::string_view a, b, c; + + template <typename Sink> + friend void AbslStringify(Sink& sink, const AbslFormatFlushTest& t) { + absl::Format(&sink, "%s, %s, %s", t.a, t.b, t.c); + } +}; +TEST(StringifyStreamTest, AbslFormatFlush) { + std::ostringstream os; + StringifyStream(os) << AbslFormatFlushTest{"a", "b", "c"}; + EXPECT_EQ(os.str(), "a, b, c"); +} + +// If overloads of both AbslStringify and operator<< are defined for the type, +// the operator<< overload should take precedence. +struct PreferStreamInsertionOverAbslStringifyTest { + friend std::ostream& operator<<( // NOLINT(clang-diagnostic-unused-function) + std::ostream& os, const PreferStreamInsertionOverAbslStringifyTest&) { + return os << "good"; + } + + template <typename Sink> + friend void AbslStringify // NOLINT(clang-diagnostic-unused-function) + (Sink& sink, const PreferStreamInsertionOverAbslStringifyTest&) { + sink.Append("bad"); + } +}; +TEST(StringifyStreamTest, PreferStreamInsertionOverAbslStringify) { + std::ostringstream os; + StringifyStream(os) << PreferStreamInsertionOverAbslStringifyTest{}; + EXPECT_EQ(os.str(), "good"); +} +TEST(StringifyStreamTest, SupportEndl) { + std::ostringstream os; + StringifyStream(os) << std::endl; + EXPECT_EQ(os.str(), "\n"); +} +TEST(StringifyStreamTest, SupportSetbase) { + std::ostringstream os; + StringifyStream(os) << std::setbase(16) << 255; + EXPECT_EQ(os.str(), "ff"); +} + +} // namespace +} // namespace strings_internal +ABSL_NAMESPACE_END +} // namespace absl
diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc index c17c472..aa22ca4 100644 --- a/absl/strings/str_split_test.cc +++ b/absl/strings/str_split_test.cc
@@ -36,6 +36,7 @@ #include "absl/container/btree_set.h" #include "absl/container/flat_hash_map.h" #include "absl/container/node_hash_map.h" +#include "absl/hash/hash.h" #include "absl/strings/string_view.h" namespace { @@ -422,6 +423,9 @@ TestConversionOperator<absl::btree_multiset<absl::string_view>>(splitter); TestConversionOperator<absl::btree_multiset<std::string>>(splitter); TestConversionOperator<std::unordered_set<std::string>>(splitter); + TestConversionOperator< + std::unordered_set<absl::string_view, absl::Hash<absl::string_view>>>( + splitter); // Tests conversion to map-like objects. @@ -455,6 +459,15 @@ splitter); TestMapConversionOperator<std::unordered_map<std::string, std::string>>( splitter); + TestMapConversionOperator<std::unordered_map< + absl::string_view, absl::string_view, absl::Hash<absl::string_view>>>( + splitter); + TestMapConversionOperator<std::unordered_map<absl::string_view, std::string, + absl::Hash<absl::string_view>>>( + splitter); + TestMapConversionOperator<std::unordered_map<std::string, absl::string_view, + absl::Hash<absl::string_view>>>( + splitter); TestMapConversionOperator< absl::node_hash_map<absl::string_view, absl::string_view>>(splitter); TestMapConversionOperator<
diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc index 0b0f920..e10d3bc 100644 --- a/absl/synchronization/internal/create_thread_identity.cc +++ b/absl/synchronization/internal/create_thread_identity.cc
@@ -77,6 +77,16 @@ identity->ticker.store(0, std::memory_order_relaxed); identity->wait_start.store(0, std::memory_order_relaxed); identity->is_idle.store(false, std::memory_order_relaxed); + // To avoid a circular dependency we declare only the storage in the header + // and use placement new to construct the SpinLock. + static_assert( + sizeof(base_internal::ThreadIdentity::SchedulerState:: + association_lock_word) == sizeof(base_internal::SpinLock), + "Wrong size for SpinLock"); + // Protects the association between this identity and its schedulable. Should + // never be cooperative. + new (&identity->scheduler_state.association_lock_word) + base_internal::SpinLock(base_internal::SCHEDULE_KERNEL_ONLY); } static void ResetThreadIdentityBetweenReuse( @@ -96,6 +106,17 @@ pts->wake = false; pts->cond_waiter = false; pts->all_locks = nullptr; + base_internal::ThreadIdentity::SchedulerState* ss = + &identity->scheduler_state; + ss->bound_schedulable.store(nullptr, std::memory_order_relaxed); + ss->association_lock_word = 0; + ss->scheduling_disabled_depth.store(0, std::memory_order_relaxed); + ss->potentially_blocking_depth = 0; + ss->schedule_next_state = 0; + ss->waking_designated_waker = false; + identity->static_initialization_depth = 0; + identity->wait_state.store(base_internal::ThreadIdentity::WaitState::kActive, + std::memory_order_relaxed); identity->blocked_count_ptr = nullptr; identity->ticker.store(0, std::memory_order_relaxed); identity->wait_start.store(0, std::memory_order_relaxed);
diff --git a/absl/synchronization/internal/per_thread_sem.h b/absl/synchronization/internal/per_thread_sem.h index 144ab3c..704f3da 100644 --- a/absl/synchronization/internal/per_thread_sem.h +++ b/absl/synchronization/internal/per_thread_sem.h
@@ -31,6 +31,10 @@ #include "absl/synchronization/internal/create_thread_identity.h" #include "absl/synchronization/internal/kernel_timeout.h" +namespace gloop_do_not_use { +struct SynchronizationBenchmarkPeer; +} // namespace gloop_do_not_use + namespace absl { ABSL_NAMESPACE_BEGIN @@ -77,6 +81,7 @@ // Permitted callers. friend class PerThreadSemTest; friend class absl::Mutex; + friend struct ::gloop_do_not_use::SynchronizationBenchmarkPeer; friend void OneTimeInitThreadIdentity(absl::base_internal::ThreadIdentity*); };
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index 9b80f1f..2016435 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc
@@ -2139,6 +2139,8 @@ intptr_t wr_wait = 0; // set to kMuWrWait if we wake a reader and a // later writer could have acquired the lock // (starvation avoidance) + // When non-null, clear its "woken_has_waiters" field before returning. + absl::base_internal::ThreadIdentity* clear_waking_des_waker = nullptr; ABSL_RAW_CHECK(waitp == nullptr || waitp->thread->waitp == nullptr || waitp->thread->suppress_fatal_errors, "detected illegal recursion into Mutex code"); @@ -2382,6 +2384,13 @@ h->readers = 0; h->maybe_unlocking = false; // finished unlocking nv |= wr_wait | kMuWait | reinterpret_cast<intptr_t>(h); + + // Signal to any Scheduler that we are waking from Mutex Unlock + // and there are more waiters left, signaling possible contention. + ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); + clear_waking_des_waker = GetOrCreateCurrentThreadIdentity(); + ABSL_TSAN_MUTEX_POST_DIVERT(this, 0); + clear_waking_des_waker->scheduler_state.waking_designated_waker = true; } // release both spinlock & lock @@ -2417,6 +2426,10 @@ ABSL_TSAN_MUTEX_POST_DIVERT(this, 0); } } + + if (clear_waking_des_waker) { + clear_waking_des_waker->scheduler_state.waking_designated_waker = false; + } } // Used by CondVar implementation to reacquire mutex after waking from
diff --git a/absl/time/simulated_clock.h b/absl/time/simulated_clock.h index 2225eee..b394750 100644 --- a/absl/time/simulated_clock.h +++ b/absl/time/simulated_clock.h
@@ -25,6 +25,7 @@ #include <optional> #include "absl/base/config.h" +#include "absl/base/macros.h" #include "absl/base/nullability.h" #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h"
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index 19709a5..769f830 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel
@@ -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( @@ -52,11 +53,13 @@ cc_library( name = "source_location", + srcs = ["source_location.cc"], hdrs = ["source_location.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", + "//absl/base:core_headers", "//absl/base:nullability", ], ) @@ -120,6 +123,62 @@ ) 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:core_headers", + "//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"], copts = ABSL_DEFAULT_COPTS,
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index f69a1ea..fd2f92f 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt
@@ -29,6 +29,44 @@ 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::core_headers + 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 HDRS "span.h" @@ -132,10 +170,13 @@ source_location HDRS "source_location.h" + SRCS + "source_location.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS absl::config + absl::core_headers absl::nullability PUBLIC )
diff --git a/absl/types/any_span.h b/absl/types/any_span.h new file mode 100644 index 0000000..98ad4f9 --- /dev/null +++ b/absl/types/any_span.h
@@ -0,0 +1,1063 @@ +// 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: any_span.h +// ----------------------------------------------------------------------------- +// +// AnySpan provides a view of a random access container, much like absl::Span +// (go/totw/93). See also go/totw/145#gtlanyspan for an introduction of AnySpan. +// +// The primary differences from absl::Span are: +// * AnySpan works with any random access container, whereas Span only works if +// elements are contiguous in memory -- both will work with std::vector, but +// only AnySpan will work with std::deque. +// * AnySpan performs a variety of transformations, such as dereferencing +// containers of pointers, or accessing specific members from a collection of +// structs, whereas Span does not offer such capability. For example, +// AnySpan<std::string> can handle both std::vector<std::string> and +// std::vector<std::string*>. Safe implicit conversions for a container's +// value type (such as up-casting from child classes, or converting +// reference_wrapper<T> to const T&) will happen implicitly. +// * AnySpan's generality has some small runtime cost, usually only a +// conditional branch per element access, or a function-pointer call in the +// worst case. Span may be preferable when the inputs are likely to be +// contiguous and performance is critical. +// +// AnySpan<T> is a mutable view to the elements and AnySpan<const T> is a +// read-only view to the elements, similar to absl::Span. +// +// AnySpan only requires containers to provide a size() and an operator[] that +// returns a reference. It will use data() if it returns a pointer to the type +// returned by operator[], which allows it to perform some internal +// optimizations (this should apply to many well behaved random access +// containers that use arrays internally, but notably +// RepeatedPtrField<T>::data() returns T** instead of T*). +// +// Using AnySpan as an input parameter: +// +// To write a function that can accept vector<MyMessage>, +// vector<unique_ptr<MyMessage>>, or RepeatedPtrField<MyMessage> as inputs, you +// can use AnySpan as the input to the function. AnySpan should be passed by +// value and it is trivially copyable so it does not need to be moved: +// +// void MyFunction(AnySpan<const MyMessage> messages); +// +// You can invoke MyFunction with a vector<MyMessage> or deque<MyMessage>: +// +// std::vector<MyMessage> messages = ...; +// MyFunction(messages); +// +// Or a container of smart pointers: +// +// std::deque<std::unique_ptr<MyMessage>> message_ptrs = ...; +// MyFunction(AnySpan<const MyMessage>( +// message_ptrs, any_span_transform::Deref())); +// +// Or, you can call the same function with a repeated proto field of type +// MyMessage: +// +// OtherMessage proto_message = ...; +// MyFunction(proto_message.repeated_field()); +// +// +// Using AnySpan as an output parameter: +// +// To write a function that allows mutation of a fixed-size container of +// objects, you can use AnySpan with a non-const value type. +// +// void MyMutatingFunction(AnySpan<MyMessage> messages); +// +// To bind a mutable AnySpan to a container, callers must construct it +// explicitly around an lvalue: +// +// std::vector<MyMessage> messages = ...; +// MyMutatingFunction(AnySpan<MyMessages>(messages)); +// +// Or use one of the "Make" functions: +// +// std::vector<MyMessage*> message_ptrs = ...; +// MyMutatingFunction(MakeDerefAnySpan(message_ptrs)); +// +// Or, if you are already dealing with a mutable view-like object, construction +// can usually be implicit: +// +// absl::Span<MyMessage> mutable_span = ...; +// MyMutatingFunction(mutable_span); +// +// Transforming Spans: +// +// A set of useful transformation functors are provided (see the +// any_span_transform namespace), but you can provide your own transforms as +// well. +// +// Transforms work for both mutable and const values. When a transform is used +// for a mutable AnySpan, it will usually have to accept its argument as a +// mutable reference. +// +// Transforms can be any object supported by std::invoke, such as +// callable objects, function pointers, member function pointers, and even data +// members. Invoking a transform must return a reference to T or a reference to +// a compatible object such as a std::reference_wrapper or a child class. +// Transforms that return value types will not compile and would return +// dangling references if they did. +// +// struct MyStruct { +// int member; +// } +// +// std::vector<MyStruct> structs = ...; +// +// // Create an AnySpan<const int> that accesses the members of 'structs': +// auto mem_ptr = &MyStruct::member; +// AnySpan<const int> members(structs, mem_ptr); +// +// // Or, using a lambda: +// auto get_member = [](const MyStruct& s) -> const int& { +// return s.member; +// }; +// AnySpan<const int> members_from_lambda(structs, get_member); +// +// Transforms must outlive the spans that use them (even member/method pointers, +// but not function pointer). Callable transforms must provide a const call +// operator that takes a single argument and returns a reference. Transforms +// will be executed every time an element is accessed, so complex transforms may +// have significant performance consequences. +// +// Factory Functions: +// +// A set of useful functions for constructing common types of AnySpans are +// provided. Factories with "Const" in the name produce AnySpans of const +// elements. Factories with "Deref" in the name will dereference elements of the +// container or array: +// +// AnySpan<T> MakeAnySpan(Container& c); +// AnySpan<T> MakeDerefAnySpan(Container& c); +// AnySpan<T> MakeAnySpan(T* ptr, std::size_t size); +// AnySpan<const T> MakeConstAnySpan(const Container& c); +// AnySpan<const T> MakeConstDerefAnySpan(const Container& c); +// AnySpan<const T> MakeConstAnySpan(const T* ptr, std::size_t size); +// +// Lifetime Gotchas: +// +// Take care when constructing spans as named variables! AnySpan captures all +// arguments by reference, even if it's a pointer: +// +// AnySpan<T> span(v, &MyClass::SomeMethod); // Dangling reference! +// +// // Also bad! The lambda is destroyed before the span. +// AnySpan<T> span(v, [](U& u) { return SomeFunction(u); }); +// +// Free functions are ok: +// +// AnySpan<T> span(v, SomeFunction); // This is OK. +// AnySpan<T> span(v, &SomeFunction); // This is OK too. +// +// In all other cases, you must ensure that the object used as a transform +// outlives the span, even if that object is a pointer type. +// +// AnySpan is also capable of capturing another AnySpan, so watch out for +// implicit conversions between types of AnySpans: +// +// // MakeDerefAnySpan() returns an AnySpan<Derived>, leaving 's' pointing to +// // a temporary! +// vector<Derived*> v; +// AnySpan<Base> s = MakeDerefAnySpan(v); +// +// Adapting Spans: +// +// Since AnySpan only expects operator[] and size(), it is relatively simple to +// write light-weight adaptor classes that can behave like containers. See the +// any_span_adaptor namespace for a utility class that does this for iterators +// and views. +// +// Adapters are more powerful than transforms, since they allow you to change +// the value type and element order of a container, but transforms will +// generally perform better and leave code with fewer object lifetime concerns. +// +// +// Note about RepeatedPtrField performance: +// +// AnySpan will use data() when it returns a pointer to the same type returned +// by operator[], however RepeatedPtrField's operator[] returns T& and its +// data() returns a T**. Because of this, AnySpan will fall back to a less +// efficient version of type-erasure. If you have a performance critical use of +// RepeatedPtrField, you might find this pattern to have better performance: +// +// MyFunction(AnySpan<const MyMessage>( +// proto_message.repeated_field().data(), +// proto_message.repeated_field().size(), +// any_span_transform::Deref())); +// +#ifndef ABSL_TYPES_ANY_SPAN_H_ +#define ABSL_TYPES_ANY_SPAN_H_ + +#include <algorithm> +#include <cstddef> +#include <functional> +#include <initializer_list> +#include <iterator> +#include <type_traits> +#include <utility> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/base/nullability.h" +#include "absl/base/optimization.h" +#include "absl/base/throw_delegate.h" +#include "absl/meta/type_traits.h" +#include "absl/types/internal/any_span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// The accessors in the 'any_span_transform' namespace return references to +// Transform functors that may be passed to AnySpan. Generally you should +// prefer to use these functors whenever possible, as they may trigger internal +// optimizations that are otherwise not possible, and they are valid for the +// duration of the program, so you do not have to worry about their lifetime. +namespace any_span_transform { + +// +// Identity() returns a functor that returns whatever is passed to it. Generally +// you should prefer to use AnySpan's implicit constructor directly, but this +// may be useful if you are writing templates on top of AnySpan. +// +// Returns a const reference so that callers don't have to worry about +// lifetime of the functor. +// + +struct IdentityT { + template <typename T> + T& operator()(T& v) const { // NOLINT(runtime/references) + return v; + } +}; + +inline const IdentityT& Identity() { + static const IdentityT f = {}; + return f; +} + +struct DerefT { + template <typename Ptr> + auto operator()(Ptr& ptr) const // NOLINT(runtime/references) + -> decltype(*ptr) { + ABSL_RAW_DCHECK(ptr, "Cannot dereference null pointer"); + return *ptr; + } +}; + +// Deref() returns a functor that dereferences whatever is passed to it. It +// works for smart and raw pointers, as well as std::optional. Do not use this +// with containers that may contain elements that cannot be dereferenced, such +// as null pointers. +// +// Returns a const reference so that callers don't have to worry about lifetime +// of the functor. +inline const DerefT& Deref() { + static const DerefT f = {}; + return f; +} + +} // namespace any_span_transform + +// Utilities for adapting things to look like the interface that AnySpan +// expects. For the most part this is based on iterators and views, and is +// intended to be composed with absl/types/iterator_adaptors.h. +namespace any_span_adaptor { + +// Adapts a pair of iterators into a container-like object that AnySpan can +// wrap. This is useful if you are faced with a range or view of random access +// iterators. Iter must be a valid random access iterator. +template <typename Iter> +class Range { + public: + static_assert( + std::is_same<typename std::iterator_traits<Iter>::iterator_category, + std::random_access_iterator_tag>::value, + "Iter must be a random access iterator."); + + Range(Iter begin, Iter end) { + ABSL_HARDENING_ASSERT(begin <= end); + begin_ = begin; + end_ = end; + } + + std::size_t size() const { return end_ - begin_; } + + decltype(std::declval<Iter>()[0]) operator[](std::size_t i) const { + ABSL_HARDENING_ASSERT(i < (end_ - begin_)); + return begin_[i]; + } + + private: + Iter begin_; + Iter end_; +}; + +// Returns a Range adaptor that wraps the given pair of iterators. The return +// value of this function must outlive any spans that use it. Iter must be a +// valid random access iterator. +template <typename Iter> +Range<Iter> MakeAdaptorFromRange(Iter begin, Iter end) { + return Range<Iter>(begin, end); +} + +// Returns a Range adaptor that wraps the given view. The begin() and end() +// functions of the given view must return valid random access iterators. The +// return value of this function must outlive any spans that use it. +template <typename View> +auto MakeAdaptorFromView(View& view) // NOLINT(runtime/references) + -> Range<decltype(view.begin())> { + return Range<decltype(view.begin())>(view.begin(), view.end()); +} + +} // namespace any_span_adaptor + +template <typename T> +class AnySpan; + +template <typename T> +class ABSL_ATTRIBUTE_VIEW AnySpan { + private: + template <typename Iter, typename Value> + class IteratorBase; + + template <typename U> + using EnableIfMutable = std::enable_if_t<!std::is_const<T>::value, U>; + + template <typename U> + using EnableIfConst = std::enable_if_t<std::is_const<T>::value, U>; + + static std::true_type CreatesATemporaryImpl(std::decay_t<T>&&); + static std::false_type CreatesATemporaryImpl(const T&); + template <typename U, + typename B = decltype(CreatesATemporaryImpl(std::declval<U>()))> + struct CreatesATemporary : B {}; + + // Enable if invoke(transform, element) is valid and if a reference to T can + // bind to its output. This prevents situations where the constructor may be + // ambiguous. + // We also verify that the conversion from TransformResult to T& does not + // create a temporary. Otherwise, we would get a false positive in the + // enabler where `const char*` looks like can be converted to + // `const std::string&`. + template <typename Transform, typename Element, + typename TransformResult = decltype(std::invoke( + std::declval<const Transform&>(), std::declval<Element>()))> + using EnableIfTransformIsValid = + std::enable_if_t<std::is_convertible_v<TransformResult, T&> && + !CreatesATemporary<TransformResult>::value>; + + // Enable if Container appears to be a valid container. Just checks for size() + // and makes sure the class is not an AnySpan for now. + template <typename Container> + using EnableIfContainer = + std::enable_if_t<any_span_internal::HasSize<Container>::value && + !any_span_internal::IsAnySpan<Container>::value>; + + template <typename Element> + using EnableIfDifferentElementType = + std::enable_if_t<!std::is_same<T, Element>::value && + !std::is_same<T, const Element>::value>; + + template <typename Transform> + using EnableIfTransformIsByCopy = + std::enable_if_t<any_span_internal::kIsTransformCopied<Transform>, bool>; + template <typename Transform> + using EnableIfTransformIsByRef = + std::enable_if_t<!any_span_internal::kIsTransformCopied<Transform>, bool>; + + public: + using element_type = T; + using value_type = typename std::remove_const<T>::type; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using absl_internal_is_view = std::true_type; + + static const size_type npos = static_cast<size_type>(-1); // NOLINT + + using reference = T&; + using const_reference = typename std::add_const<T>::type&; + + using pointer = T*; + using const_pointer = typename std::add_const<T>::type*; + + // Note that iterator will be const if T is const. + class iterator; + class const_iterator; + + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + // Null and empty by default. + AnySpan() = default; + + // Creates a span that wraps an initializer list. This makes it possible to + // pass a brace-enclosed initializer list to a function expecting an AnySpan. + // + // Example: + // + // void Process(AnySpan<const int> x); + // Process({1, 2, 3}); + // + // The initializer_list must outlive this AnySpan. + constexpr AnySpan( // NOLINT(google-explicit-constructor) + std::initializer_list<value_type> l ABSL_ATTRIBUTE_LIFETIME_BOUND) + : AnySpan(l.begin(), l.size()) {} + + // Creates a span that wraps an initializer list of a type other than + // value_type, or with an explicit transform. Applies the optional transform + // to elements before returning them. + // + // Example: + // + // struct Base {}; + // struct Derived : Base {}; + // + // void Process(AnySpan<const Base> x); + // Process({Derived(a), Derived(b), Derived(c)}); + // + // where the default identity transform would apply an implicit + // derived-to-base conversion. + // + // The initializer_list must outlive this AnySpan. + template <typename Element, typename Transform, + typename = EnableIfTransformIsValid<Transform, const Element&>, + EnableIfTransformIsByCopy<Transform> = true> + constexpr AnySpan(std::initializer_list<Element> l + ABSL_ATTRIBUTE_LIFETIME_BOUND, + const Transform& transform) + : AnySpan(l.begin(), l.size(), transform) {} + template <typename Element, + typename Transform = any_span_transform::IdentityT, + typename = EnableIfTransformIsValid<Transform, const Element&>, + EnableIfTransformIsByRef<Transform> = true> + constexpr AnySpan(std::initializer_list<Element> l + ABSL_ATTRIBUTE_LIFETIME_BOUND, + const Transform& transform ABSL_ATTRIBUTE_LIFETIME_BOUND = + any_span_transform::Identity()) + : AnySpan(l.begin(), l.size(), transform) {} + + // Creates a span that wraps an array. Applies the optional transform to + // elements before returning them. + // + // Transform must be a function object with a const operator() that takes + // Element as an argument and return a reference to T or compatible object. + // + // Both the transform and array must outlive this span. + template <typename Element, typename Transform, + typename = EnableIfTransformIsValid<Transform, const Element&>, + EnableIfTransformIsByCopy<Transform> = true> + constexpr AnySpan(const Element* absl_nullable ptr + ABSL_ATTRIBUTE_LIFETIME_BOUND, + size_type size, const Transform& transform) + : AnySpan(any_span_internal::MakeArrayGetter<T>(ptr, transform), size) {} + template <typename Element, + typename Transform = any_span_transform::IdentityT, + typename = EnableIfTransformIsValid<Transform, const Element&>, + EnableIfTransformIsByRef<Transform> = true> + constexpr AnySpan(const Element* absl_nullable ptr + ABSL_ATTRIBUTE_LIFETIME_BOUND, + size_type size, + const Transform& transform ABSL_ATTRIBUTE_LIFETIME_BOUND = + any_span_transform::Identity()) + : AnySpan(any_span_internal::MakeArrayGetter<T>(ptr, transform), size) {} + + // Creates a span that wraps an array of fixed size. Applies the optional + // transform to elements before returning them. + // + // Transform must be a function object with a const operator() that takes + // Element as an argument and return a reference to T or compatible object. + // + // Both the transform and array must outlive this span. + template <typename Element, size_type N, typename Transform, + typename = EnableIfTransformIsValid<Transform, const Element&>, + EnableIfTransformIsByCopy<Transform> = true> + constexpr AnySpan( // NOLINT(google-explicit-constructor) + const Element (&array ABSL_ATTRIBUTE_LIFETIME_BOUND)[N], + const Transform& transform) + : AnySpan(array, N, transform) {} + template <typename Element, size_type N, + typename Transform = any_span_transform::IdentityT, + typename = EnableIfTransformIsValid<Transform, const Element&>, + EnableIfTransformIsByRef<Transform> = true> + constexpr AnySpan( // NOLINT(google-explicit-constructor) + const Element (&array ABSL_ATTRIBUTE_LIFETIME_BOUND)[N], + const Transform& transform ABSL_ATTRIBUTE_LIFETIME_BOUND = + any_span_transform::Identity()) + : AnySpan(array, N, transform) {} + + // Creates a span that wraps a const container. Applies the optional transform + // to elements before returning them. + // + // This constructor is enabled even for mutable spans, since some + // container-like objects provide mutable element access even when the object + // itself is const (such as absl::Span) + // + // Transform must be a function object with a const operator() that takes the + // value type of Container as an argument and return a reference to T or + // compatible object. + // + // The transform, container, and the container's underlying storage must + // outlive this span. Any operation that may reallocate the container's + // storage or change its size will invalidate the span. + template <typename Container, typename Transform, + typename = EnableIfContainer<Container>, + typename = EnableIfTransformIsValid< + Transform, decltype(std::declval<const Container&>()[0])>, + EnableIfTransformIsByCopy<std::enable_if_t< + absl::type_traits_internal::IsView<Container>::value, + Transform>> = true> + constexpr AnySpan( // NOLINT(google-explicit-constructor) + const Container& container, const Transform& transform) + : AnySpan(any_span_internal::MakeContainerGetter<T>(container, transform), + container.size()) {} + template <typename Container, typename Transform, + typename = EnableIfContainer<Container>, + typename = EnableIfTransformIsValid< + Transform, decltype(std::declval<const Container&>()[0])>, + EnableIfTransformIsByCopy<std::enable_if_t< + !absl::type_traits_internal::IsView<Container>::value, + Transform>> = true> + constexpr AnySpan( // NOLINT(google-explicit-constructor) + const Container& container ABSL_ATTRIBUTE_LIFETIME_BOUND, + const Transform& transform) + : AnySpan(any_span_internal::MakeContainerGetter<T>(container, transform), + container.size()) {} + template < + typename Container, typename Transform = any_span_transform::IdentityT, + typename = EnableIfContainer<Container>, + typename = EnableIfTransformIsValid< + Transform, decltype(std::declval<const Container&>()[0])>, + EnableIfTransformIsByRef< + std::enable_if_t<absl::type_traits_internal::IsView<Container>::value, + Transform>> = true> + constexpr AnySpan( // NOLINT(google-explicit-constructor) + const Container& container, + const Transform& transform ABSL_ATTRIBUTE_LIFETIME_BOUND = + any_span_transform::Identity()) + : AnySpan(any_span_internal::MakeContainerGetter<T>(container, transform), + container.size()) {} + template <typename Container, + typename Transform = any_span_transform::IdentityT, + typename = EnableIfContainer<Container>, + typename = EnableIfTransformIsValid< + Transform, decltype(std::declval<const Container&>()[0])>, + EnableIfTransformIsByRef<std::enable_if_t< + !absl::type_traits_internal::IsView<Container>::value, + Transform>> = true> + constexpr AnySpan( // NOLINT(google-explicit-constructor) + const Container& container ABSL_ATTRIBUTE_LIFETIME_BOUND, + const Transform& transform ABSL_ATTRIBUTE_LIFETIME_BOUND = + any_span_transform::Identity()) + : AnySpan(any_span_internal::MakeContainerGetter<T>(container, transform), + container.size()) {} + + // Creates a span that wraps a mutable array. Applies the optional transform + // to elements before returning them. + // + // Transform must be a function object with a const operator() that takes + // Element as an argument and return a reference to T or compatible object. + // + // Both the transform and array must outlive this span. + template <typename Element, typename Transform, + typename = EnableIfMutable<Element>, + typename = EnableIfTransformIsValid<Transform, Element&>, + EnableIfTransformIsByCopy<Transform> = true> + constexpr AnySpan(Element* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND, + size_type size, const Transform& transform) + : AnySpan(any_span_internal::MakeArrayGetter<T>(ptr, transform), size) {} + template <typename Element, + typename Transform = any_span_transform::IdentityT, + typename = EnableIfMutable<Element>, + typename = EnableIfTransformIsValid<Transform, Element&>, + EnableIfTransformIsByRef<Transform> = true> + constexpr AnySpan(Element* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND, + size_type size, + const Transform& transform ABSL_ATTRIBUTE_LIFETIME_BOUND = + any_span_transform::Identity()) + : AnySpan(any_span_internal::MakeArrayGetter<T>(ptr, transform), size) {} + + // Creates a span that wraps a mutable array of fixed size. Applies the + // optional transform to elements before returning them. + // + // Transform must be a function object with a const operator() that takes + // Element as an argument and return a reference to T or compatible object. + // + // Both the transform and array must outlive this span. + template <typename Element, size_type N, typename Transform, + typename = EnableIfMutable<Element>, + typename = EnableIfTransformIsValid<Transform, Element&>, + EnableIfTransformIsByCopy<Transform> = true> + constexpr AnySpan( // NOLINT(google-explicit-constructor) + Element (&array ABSL_ATTRIBUTE_LIFETIME_BOUND)[N], + const Transform& transform) + : AnySpan(array, N, transform) {} + template <typename Element, size_type N, + typename Transform = any_span_transform::IdentityT, + typename = EnableIfMutable<Element>, + typename = EnableIfTransformIsValid<Transform, Element&>, + EnableIfTransformIsByRef<Transform> = true> + constexpr AnySpan( // NOLINT(google-explicit-constructor) + Element (&array ABSL_ATTRIBUTE_LIFETIME_BOUND)[N], + const Transform& transform ABSL_ATTRIBUTE_LIFETIME_BOUND = + any_span_transform::Identity()) + : AnySpan(array, N, transform) {} + + // Creates a span that wraps a mutable container. Only enabled if T is + // mutable. Applies the optional transform to elements before returning them. + // + // Transform must be a function object with a const operator() that takes the + // value type of Container as an argument and return a reference to T or + // compatible object. + // + // The transform, container, and the container's underlying storage must + // outlive this span. Any operation that may reallocate the container's + // storage or change its size will invalidate the span. + template <typename Container, typename Transform, + typename = EnableIfMutable<Container>, + typename = EnableIfContainer<Container>, + typename = EnableIfTransformIsValid< + Transform, decltype(std::declval<Container&>()[0])>, + EnableIfTransformIsByCopy<Transform> = true> + constexpr explicit AnySpan( // NOLINT(google-explicit-constructor) + Container& container ABSL_ATTRIBUTE_LIFETIME_BOUND, + const Transform& transform) + : AnySpan(any_span_internal::MakeContainerGetter<T>(container, transform), + container.size()) {} + template <typename Container, + typename Transform = any_span_transform::IdentityT, + typename = EnableIfMutable<Container>, + typename = EnableIfContainer<Container>, + typename = EnableIfTransformIsValid< + Transform, decltype(std::declval<Container&>()[0])>, + EnableIfTransformIsByRef<Transform> = true> + constexpr explicit AnySpan( // NOLINT(google-explicit-constructor) + Container& container ABSL_ATTRIBUTE_LIFETIME_BOUND, + const Transform& transform ABSL_ATTRIBUTE_LIFETIME_BOUND = + any_span_transform::Identity()) + : AnySpan(any_span_internal::MakeContainerGetter<T>(container, transform), + container.size()) {} + + // Converts a mutable span to a const span by copying the internal state + // (rather than wrapping the other span). + // TODO(b/179783710): add ABSL_ATTRIBUTE_LIFETIME_BOUND. + template <typename LazyT = T, typename = EnableIfConst<LazyT>> + constexpr AnySpan( // NOLINT(google-explicit-constructor) + const AnySpan<typename std::remove_const<T>::type>& other) + : getter_(other.getter_), size_(other.size()) {} + + // Creates a span that wraps around another span of different type. + // + // This has performance and lifetime consequences, and can easily happen by + // mistake. We make such conversions explicit here. + template <typename Element, typename = EnableIfDifferentElementType<Element>, + typename = EnableIfTransformIsValid<any_span_transform::IdentityT, + Element&>> + constexpr explicit AnySpan( + const AnySpan<Element>& other ABSL_ATTRIBUTE_LIFETIME_BOUND) + : AnySpan(any_span_internal::MakeContainerGetter<T>( + other, any_span_transform::Identity()), + other.size()) {} + + // Creates a span that wraps around another span. Applies the non-optional + // transform to elements before returning them. + // + // This has lifetime consequences, and may happen by mistake. We make it + // explicit here. + template <typename Element, typename Transform, + typename = EnableIfTransformIsValid<Transform, Element&>, + EnableIfTransformIsByCopy<Transform> = true> + constexpr explicit AnySpan(const AnySpan<Element>& other + ABSL_ATTRIBUTE_LIFETIME_BOUND, + const Transform& transform) + : AnySpan(any_span_internal::MakeContainerGetter<T>(other, transform), + other.size()) {} + template <typename Element, typename Transform, + typename = EnableIfTransformIsValid<Transform, Element&>, + EnableIfTransformIsByRef<Transform> = true> + constexpr explicit AnySpan( + const AnySpan<Element>& other ABSL_ATTRIBUTE_LIFETIME_BOUND, + const Transform& transform ABSL_ATTRIBUTE_LIFETIME_BOUND) + : AnySpan(any_span_internal::MakeContainerGetter<T>(other, transform), + other.size()) {} + + // Returns a subspan of this span. This span may become invalid before the + // subspan, but both the container and transform must remain valid. + // pos must be non-negative and <= size(). + // len must be non-negative and <= size() - pos, or equal to npos. + // If len == npos, the subspan continues till the end of this span. + + constexpr AnySpan subspan(size_type pos, size_type len) const { + const size_t this_size = size(); + if (len == AnySpan<T>::npos) { + len = this_size - pos; + } + ABSL_HARDENING_ASSERT(pos <= this_size && len <= this_size - pos); + return AnySpan<T>(getter_.Offset(pos), len); + } + + constexpr AnySpan subspan(size_type pos) const { + ABSL_HARDENING_ASSERT(pos <= size()); + return AnySpan(getter_.Offset(pos), size() - pos); + } + + // Returns a `AnySpan` containing first `len` elements. Parameter `len` + // must be non-negative and <= size(). + constexpr AnySpan first(size_type len) const { + ABSL_HARDENING_ASSERT(len != AnySpan<T>::npos); + return subspan(0, len); + } + + // Returns a `AnySpan` containing last `len` elements. Parameter `len` must be + // non-negative and <= size(). + constexpr AnySpan last(size_type len) const { return subspan(size() - len); } + + // Size operations. + constexpr size_type size() const { return size_; } + constexpr bool empty() const { return size() == 0; } + + // Element access. + constexpr reference operator[](size_type index) const { + ABSL_HARDENING_ASSERT(index < size()); + return getter_.Get(index); + } + constexpr reference at(size_type index) const { + if (ABSL_PREDICT_FALSE(index >= size())) { + absl::ThrowStdOutOfRange("AnySpan::at failed bounds check"); + } + return getter_.Get(index); + } + constexpr reference front() const { + ABSL_HARDENING_ASSERT(size() > 0); + return (*this)[0]; + } + constexpr reference back() const { + ABSL_HARDENING_ASSERT(size() > 0); + return (*this)[size() - 1]; + } + + // Iterator accessors. + constexpr iterator begin() const { return iterator(this, 0); } + constexpr iterator end() const { return iterator(this, size_); } + constexpr reverse_iterator rbegin() const { return reverse_iterator(end()); } + constexpr reverse_iterator rend() const { return reverse_iterator(begin()); } + constexpr const_iterator cbegin() const { return const_iterator(this, 0); } + constexpr const_iterator cend() const { return const_iterator(this, size_); } + constexpr const_reverse_iterator crbegin() const { return rbegin(); } + constexpr const_reverse_iterator crend() const { return rend(); } + + // Constructs from a getter and size. Not for external use. + AnySpan(any_span_internal::Getter<T> getter, size_type size) + : getter_(getter), size_(size) {} + + // Support for absl::Hash. + template <typename H> + friend constexpr H AbslHashValue(H state, AnySpan any_span) { + for (const auto& v : any_span) { + state = H::combine(std::move(state), v); + } + return H::combine(std::move(state), any_span.size()); + } + + private: + template <typename U> + friend class AnySpan; + + template <typename U> + friend bool any_span_internal::IsCheap(AnySpan<U> s); + + // Getter to access elements. + any_span_internal::Getter<T> getter_; + + // The size of this span. + size_type size_ = 0; + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-template-friend" +#endif + // The technical reasons we need to declare these friends in this manner are + // quite subtle and confusing, but they're necessary on some toolchains to + // allow all mutable/const combinations with this & other range types while + // avoiding symbol collisions or ODR violations. + friend bool operator==(AnySpan<const T> a, AnySpan<const T> b); + friend bool operator!=(AnySpan<const T> a, AnySpan<const T> b); +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + // operator== + friend bool operator==(AnySpan a, AnySpan b) { + return any_span_internal::EqualImpl<const T>(a, b); + } + friend bool operator!=(AnySpan a, AnySpan b) { return !(a == b); } +}; + +// Constructs an AnySpan from a container or array. +template <int&... ExplicitArgumentBarrier, typename Container, + typename T = any_span_internal::ElementType<Container>> +std::enable_if_t< + absl::type_traits_internal::IsView<std::remove_cv_t<Container>>::value, + AnySpan<T>> +MakeAnySpan(Container& c) { + return AnySpan<T>(c); +} +template <int&... ExplicitArgumentBarrier, typename Container, + typename T = any_span_internal::ElementType<Container>> +std::enable_if_t< + !absl::type_traits_internal::IsView<std::remove_cv_t<Container>>::value, + AnySpan<T>> +MakeAnySpan(Container& c ABSL_ATTRIBUTE_LIFETIME_BOUND) { + return AnySpan<T>(c); +} + +// Constructs an AnySpan that dereferences a container or array of pointers. +template <int&... ExplicitArgumentBarrier, typename Container, + typename T = any_span_internal::DerefElementType<Container>> +std::enable_if_t< + absl::type_traits_internal::IsView<std::remove_cv_t<Container>>::value, + AnySpan<T>> +MakeDerefAnySpan(Container& c) { + return AnySpan<T>(c, any_span_transform::Deref()); +} +template <int&... ExplicitArgumentBarrier, typename Container, + typename T = any_span_internal::DerefElementType<Container>> +std::enable_if_t< + !absl::type_traits_internal::IsView<std::remove_cv_t<Container>>::value, + AnySpan<T>> +MakeDerefAnySpan(Container& c ABSL_ATTRIBUTE_LIFETIME_BOUND) { + return AnySpan<T>(c, any_span_transform::Deref()); +} + +// Constructs an AnySpan from a pointer and size. +template <int&... ExplicitArgumentBarrier, typename T> +AnySpan<T> MakeAnySpan(T* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND, + std::size_t size) { + return AnySpan<T>(ptr, size); +} + +// Constructs a const AnySpan from a container or array. +template <int&... ExplicitArgumentBarrier, typename Container, + typename T = any_span_internal::ElementType<const Container>> +std::enable_if_t<absl::type_traits_internal::IsView<Container>::value, + AnySpan<const T>> +MakeConstAnySpan(const Container& c) { + return AnySpan<const T>(c); +} +template <int&... ExplicitArgumentBarrier, typename Container, + typename T = any_span_internal::ElementType<const Container>> +std::enable_if_t<!absl::type_traits_internal::IsView<Container>::value, + AnySpan<const T>> +MakeConstAnySpan(const Container& c ABSL_ATTRIBUTE_LIFETIME_BOUND) { + return AnySpan<const T>(c); +} + +// Constructs a const AnySpan that dereferences a container or array of +// pointers. +template <int&... ExplicitArgumentBarrier, typename Container, + typename T = any_span_internal::DerefElementType<const Container>> +std::enable_if_t<absl::type_traits_internal::IsView<Container>::value, + AnySpan<const T>> +MakeConstDerefAnySpan(const Container& c) { + return AnySpan<const T>(c, any_span_transform::Deref()); +} +template <int&... ExplicitArgumentBarrier, typename Container, + typename T = any_span_internal::DerefElementType<const Container>> +std::enable_if_t<!absl::type_traits_internal::IsView<Container>::value, + AnySpan<const T>> +MakeConstDerefAnySpan(const Container& c ABSL_ATTRIBUTE_LIFETIME_BOUND) { + return AnySpan<const T>(c, any_span_transform::Deref()); +} + +// Constructs an AnySpan from a pointer and size. +template <int&... ExplicitArgumentBarrier, typename T> +AnySpan<const T> MakeConstAnySpan(const T* absl_nullable ptr, + std::size_t size) { + return AnySpan<const T>(ptr, size); +} + +// +// Implementation details follow. +// + +template <typename T> +const typename AnySpan<T>::size_type AnySpan<T>::npos; + +// Iterator base class. Uses CRTP (Iter should be the child class). Constness of +// the iterator is determined by the constness of Value. +template <typename T> +template <typename Iter, typename Value> +class ABSL_ATTRIBUTE_VIEW AnySpan<T>::IteratorBase { + private: + // Returns a reference to this as the child class. + const Iter& self() const { return static_cast<const Iter&>(*this); } + Iter& self() { return static_cast<Iter&>(*this); } + + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = typename std::remove_const<Value>::type; + using difference_type = std::ptrdiff_t; + using reference = Value&; + using pointer = Value*; + + // Constructs an invalid iterator. + IteratorBase() = default; + + reference operator*() const { return (*container_)[index_]; } + + pointer absl_nonnull operator->() const { return &(*container_)[index_]; } + + reference operator[](difference_type i) const { + return (*container_)[index_ + i]; + } + + Iter& operator+=(difference_type d) { + index_ += d; + return self(); + } + + Iter& operator-=(difference_type d) { return self() += -d; } + + Iter& operator++() { + self() += 1; + return self(); + } + + Iter operator++(int) { + Iter copy(self()); + ++self(); + return copy; + } + + Iter& operator--() { + self() -= 1; + return self(); + } + + Iter operator--(int) { + Iter copy(self()); + --self(); + return copy; + } + + Iter operator+(difference_type d) const { + Iter tmp = self(); + tmp += d; + return tmp; + } + + friend Iter operator+(difference_type d, Iter i) { return i + d; } + + Iter operator-(difference_type d) const { return self() + (-d); } + + difference_type operator-(const Iter& other) const { + return index_ - other.index_; + } + + friend bool operator==(const Iter& a, const Iter& b) { + return a.index_ == b.index_; + } + + friend bool operator!=(const Iter& a, const Iter& b) { + return a.index_ != b.index_; + } + + friend bool operator<(const Iter& a, const Iter& b) { + return a.index_ < b.index_; + } + + friend bool operator<=(const Iter& a, const Iter& b) { + return a.index_ <= b.index_; + } + + friend bool operator>(const Iter& a, const Iter& b) { + return a.index_ > b.index_; + } + + friend bool operator>=(const Iter& a, const Iter& b) { + return a.index_ >= b.index_; + } + + protected: + // Constructs an iterator that points to the given index of the given span. + IteratorBase(const AnySpan* absl_nullable container, size_type index) + : container_(container), index_(index) {} + + const AnySpan* absl_nullable container_ = nullptr; + size_type index_ = 0; +}; + +// iterator implementation. This mostly just forwards to IteratorBase. +template <typename T> +class ABSL_ATTRIBUTE_VIEW AnySpan<T>::iterator + : public IteratorBase<iterator, T> { + private: + using Base = IteratorBase<iterator, T>; + + public: + using typename Base::difference_type; + using typename Base::iterator_category; + using typename Base::pointer; + using typename Base::reference; + using typename Base::value_type; + + iterator() = default; + + private: + // Only let AnySpan construct valid instances. + friend class AnySpan; + + iterator(const AnySpan* absl_nullable container, size_type index) + : Base(container, index) {} +}; + +// const_iterator implementation. This mostly just forwards to IteratorBase, +// but also provides conversion from MutableIterator. +template <typename T> +class AnySpan<T>::const_iterator + : public IteratorBase<const_iterator, typename std::add_const<T>::type> { + private: + using Base = IteratorBase<const_iterator, typename std::add_const<T>::type>; + + public: + using typename Base::difference_type; + using typename Base::iterator_category; + using typename Base::pointer; + using typename Base::reference; + using typename Base::value_type; + + const_iterator() = default; + + // Support conversion from mutable iterators. + // NOLINTNEXTLINE(google-explicit-constructor) + const_iterator(const iterator& other) // NOLINT(runtime/explicit) + : Base(other.container_, other.index_) {} + + private: + // Only let AnySpan construct valid instances. + friend class AnySpan; + + const_iterator(const AnySpan* absl_nullable container, size_type index) + : Base(container, index) {} +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_TYPES_ANY_SPAN_H_
diff --git a/absl/types/any_span_benchmark.cc b/absl/types/any_span_benchmark.cc new file mode 100644 index 0000000..24e9644 --- /dev/null +++ b/absl/types/any_span_benchmark.cc
@@ -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
diff --git a/absl/types/any_span_test.cc b/absl/types/any_span_test.cc new file mode 100644 index 0000000..8ff1fdd --- /dev/null +++ b/absl/types/any_span_test.cc
@@ -0,0 +1,1210 @@ +// 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 "absl/types/any_span.h" + +#include <cstddef> +#include <deque> +#include <functional> +#include <iterator> +#include <memory> +#include <optional> +#include <string> +#include <string_view> +#include <type_traits> +#include <utility> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/exception_testing.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/hash/hash_testing.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace { + +using testing::ElementsAre; + +static_assert(!absl::type_traits_internal::IsOwner<AnySpan<int>>::value && + absl::type_traits_internal::IsView<AnySpan<int>>::value, + "AnySpan is a view, not an owner"); + +bool IsHardened() { + bool hardened = false; + ABSL_HARDENING_ASSERT([&hardened]() { + hardened = true; + return true; + }()); + return hardened; +} + +// Tests that the span points to all of the elements of the given container. +template <typename T, typename Container> +void ExpectSpanEqualsContainer(AnySpan<T> span, const Container& c) { + EXPECT_EQ(span.size(), c.size()); + typename AnySpan<const T>::size_type index = 0; + for (const T& s : span) { + SCOPED_TRACE(index); + const T& expected = c[index]; + EXPECT_EQ(expected, s); + EXPECT_EQ(expected, span[index]); + EXPECT_EQ(&expected, &span[index]); + ++index; + } + EXPECT_EQ(index, span.size()); +} + +// AnySpanTest covers parts of AnySpan that make sense for both const and +// non-const elements. How the given tests will run is determined by whether or +// not ElementsAreConst is true_type or false_type. +template <typename ElementsAreConst> +class AnySpanTest : public ::testing::Test { + public: + using IsConstTest = ElementsAreConst; + + // Const T if this test instantiation is supposed to cover const AnySpans. + // T if this test instantiation is supposed to cover mutable AnySpans. + template <typename T> + using MaybeConst = + typename std::conditional<IsConstTest::value, const T, T>::type; + + // Initalizes a AnySpan around the given container and tests for + // equality in a variety of ways. + template <typename T, typename Container, typename Transform> + static void TestContainer(Container c, const Transform& t) { + AnySpan<MaybeConst<T>> span(c, t); + EXPECT_NO_FATAL_FAILURE(ExpectSpanEqualsContainer(span, c)); + } + + // Initializes a dereferencing AnySpan around the given container of pointers + // and tests for equality in a variety of ways. + template <typename T, typename Container> + static void TestPointerContainer(Container* c) { + AnySpan<const T> span(*c, any_span_transform::Deref()); + EXPECT_EQ(span.size(), c->size()); + int index = 0; + for (const T& s : span) { + const T& expected = *(*c)[index]; + EXPECT_EQ(expected, s); + EXPECT_EQ(expected, span[index]); + EXPECT_EQ(&expected, &span[index]); + index++; + } + EXPECT_EQ(index, span.size()); + } +}; + +using ElementsAreConst = ::testing::Types<std::true_type, std::false_type>; + +TYPED_TEST_SUITE(AnySpanTest, ElementsAreConst); + +TYPED_TEST(AnySpanTest, NullAndEmpty) { + using T = typename TestFixture::template MaybeConst<std::string>; + AnySpan<T> empty; + EXPECT_TRUE(empty.empty()); + EXPECT_EQ(empty.size(), 0); + for (const std::string& a : empty) { + (void)a; + ABSL_RAW_LOG(FATAL, "Empty container should not iterate."); + } + EXPECT_EQ(empty.begin(), empty.end()); +} + +TYPED_TEST(AnySpanTest, Ptr) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::vector<std::string> v = {"a", "b", "c", "d"}; + AnySpan<T> span1(v.data(), v.size()); + EXPECT_EQ(&span1[0], &v[0]); + AnySpan<T> span2(v.data(), v.size()); + EXPECT_EQ(&span2[0], &v[0]); +} + +TYPED_TEST(AnySpanTest, PtrPtr) { + using T = typename TestFixture::template MaybeConst<int>; + int i; + std::vector<int*> v = {&i}; + AnySpan<T> span(v.data(), v.size(), any_span_transform::Deref()); + EXPECT_EQ(&span[0], &i); +} + +TYPED_TEST(AnySpanTest, DerefOptional) { + const std::vector<std::optional<int>> v = {17, 19}; + const AnySpan<const int> span = absl::MakeDerefAnySpan(v); + EXPECT_THAT(span, ElementsAre(17, 19)); +} + +TYPED_TEST(AnySpanTest, ArrayOfKnownSize) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::string a[] = {"a", "b", "c", "d"}; + + AnySpan<T> span(a); + EXPECT_EQ(span.size(), 4); + EXPECT_EQ(&span[0], &a[0]); +} + +// This gets its own test because AnySpan(array, size) has the potential to be +// ambiguous with AnySpan(array, transform). +TYPED_TEST(AnySpanTest, ArrayWithExplicitSize) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::string a[] = {"a", "b", "c", "d"}; + + AnySpan<T> span(a, 4); + EXPECT_EQ(span.size(), 4); + EXPECT_EQ(&span[0], &a[0]); +} + +TYPED_TEST(AnySpanTest, PtrArrayOfKnownSize) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::string s; + std::string* a[] = {&s, &s, &s, &s}; + + AnySpan<T> span(a, any_span_transform::Deref()); + EXPECT_EQ(span.size(), 4); + EXPECT_EQ(&span[0], &s); +} + +TYPED_TEST(AnySpanTest, Range) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::vector<std::string> a = {"a", "b", "c", "d"}; + auto range = any_span_adaptor::MakeAdaptorFromRange(a.begin(), a.end()); + AnySpan<T> span(range); + EXPECT_NO_FATAL_FAILURE(ExpectSpanEqualsContainer(span, a)); +} + +TYPED_TEST(AnySpanTest, View) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::vector<std::string> a = {"a", "b", "c", "d"}; + auto range = any_span_adaptor::MakeAdaptorFromView(a); + AnySpan<T> span(range); + EXPECT_NO_FATAL_FAILURE(ExpectSpanEqualsContainer(span, a)); +} + +TYPED_TEST(AnySpanTest, VectorString) { + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<std::string>( + std::vector<std::string>{"a", "b", "c", "d"}, + any_span_transform::Identity())); +} + +TYPED_TEST(AnySpanTest, VectorInt) { + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<int>( + std::vector<int>{1, 2, 3, 4}, any_span_transform::Identity())); +} + +TYPED_TEST(AnySpanTest, VectorStringPointer) { + std::vector<std::string> v = {"a", "b", "c", "d"}; + std::vector<std::string*> vp; + for (std::string& s : v) { + vp.push_back(&s); + } + TestFixture::template TestPointerContainer<std::string>(&vp); +} + +TYPED_TEST(AnySpanTest, VectorStringUniquePointer) { + std::vector<std::unique_ptr<std::string>> v; + for (const std::string& s : std::vector<std::string>{"a", "b", "c", "d"}) { + v.emplace_back(new std::string(s)); + } + TestFixture::template TestPointerContainer<std::string>(&v); +} + +TYPED_TEST(AnySpanTest, VectorIntPointer) { + std::vector<int> v = {1, 2, 3, 4}; + std::vector<int*> vp; + for (int& i : v) { + vp.push_back(&i); + } + TestFixture::template TestPointerContainer<int>(&vp); +} + +TYPED_TEST(AnySpanTest, DequeString) { + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<std::string>( + std::deque<std::string>{"a", "b", "c", "d"}, + any_span_transform::Identity())); +} + +TYPED_TEST(AnySpanTest, DequeInt) { + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<int>( + std::deque<int>{1, 2, 3, 4}, any_span_transform::Identity())); +} + +TYPED_TEST(AnySpanTest, DequeStringPtr) { + std::vector<std::string> v = {"a", "b", "c", "d"}; + std::deque<std::string*> dp; + for (std::string& s : v) { + dp.push_back(&s); + } + TestFixture::template TestPointerContainer<std::string>(&dp); +} + +TYPED_TEST(AnySpanTest, ImplicitConversions) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::string one = "1"; + std::string two = "2"; + std::vector<std::reference_wrapper<std::string>> v( + {std::ref(one), std::ref(two)}); + AnySpan<T> span(v); + + EXPECT_THAT(span, ElementsAre("1", "2")); +} + +TYPED_TEST(AnySpanTest, TrivialSubspan) { + EXPECT_EQ(AnySpan<int>().subspan(0).size(), 0); +} + +TYPED_TEST(AnySpanTest, TrivialFirst) { + using T = typename TestFixture::template MaybeConst<int>; + EXPECT_EQ(AnySpan<T>().first(0).size(), 0); +} + +TYPED_TEST(AnySpanTest, TrivialLast) { + using T = typename TestFixture::template MaybeConst<int>; + EXPECT_EQ(AnySpan<T>().last(0).size(), 0); +} + +struct Base1 { + virtual ~Base1() = default; + bool operator==(const Base1& other) const { return this == &other; } + int b1 = 1; +}; +struct Base2 { + virtual ~Base2() = default; + bool operator==(const Base2& other) const { return this == &other; } + int b2 = 2; +}; +struct Child : public Base1, public Base2 { + bool operator==(const Child& other) const { return this == &other; } +}; + +TYPED_TEST(AnySpanTest, VectorBaseClass) { + std::vector<Child> v; + v.resize(20); + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<Base1>( + v, any_span_transform::Identity())); + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<Base2>( + v, any_span_transform::Identity())); +} + +TYPED_TEST(AnySpanTest, AnySpanVectorBaseClass) { + std::vector<Child> v; + v.resize(20); + AnySpan<Child> span(v); + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<Base1>( + span, any_span_transform::Identity())); + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<Base2>( + span, any_span_transform::Identity())); +} + +TYPED_TEST(AnySpanTest, DerefAnySpanVectorBaseClass) { + Child c; + std::vector<Child*> v_ptrs(20, &c); + AnySpan<Child> span(v_ptrs, any_span_transform::Deref()); + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<Base1>( + span, any_span_transform::Identity())); + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<Base2>( + span, any_span_transform::Identity())); +} + +TYPED_TEST(AnySpanTest, AnySpanDequeBaseClass) { + std::deque<Child> v; + v.resize(20); + AnySpan<Child> span(v); + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<Base1>( + span, any_span_transform::Identity())); + EXPECT_NO_FATAL_FAILURE(TestFixture::template TestContainer<Base2>( + span, any_span_transform::Identity())); +} + +TYPED_TEST(AnySpanTest, VectorSubspan) { + using T = typename TestFixture::template MaybeConst<int>; + std::vector<int> v = {1, 2, 3, 4}; + auto span = AnySpan<T>(v).subspan(1, 2); + EXPECT_EQ(span.size(), 2); + EXPECT_EQ(span[0], 2); + EXPECT_EQ(span[1], 3); + span = AnySpan<T>(v).subspan(1); + EXPECT_EQ(span.size(), 3); + EXPECT_EQ(span[2], 4); + + std::vector<int> zero_length_vector; + auto zero_length_subspan = AnySpan<T>(zero_length_vector).subspan(0); + EXPECT_EQ(zero_length_subspan.size(), 0); +} + +TYPED_TEST(AnySpanTest, DequeSubspan) { + using T = typename TestFixture::template MaybeConst<int>; + std::deque<int> v = {1, 2, 3, 4}; + auto span = AnySpan<T>(v).subspan(1, 2); + EXPECT_EQ(span.size(), 2); + EXPECT_EQ(span[0], 2); + EXPECT_EQ(span[1], 3); +} + +TYPED_TEST(AnySpanTest, VectorFirst) { + using T = typename TestFixture::template MaybeConst<int>; + std::vector<int> v = {1, 2, 3, 4}; + auto span = AnySpan<T>(v).first(2); + EXPECT_THAT(span, ElementsAre(1, 2)); + span = AnySpan<T>(v).first(4); + EXPECT_THAT(span, ElementsAre(1, 2, 3, 4)); + + std::vector<int> zero_length_vector; + auto zero_length_subspan = AnySpan<T>(zero_length_vector).first(0); + EXPECT_EQ(zero_length_subspan.size(), 0); +} + +TYPED_TEST(AnySpanTest, DequeFirst) { + using T = typename TestFixture::template MaybeConst<int>; + std::deque<int> v = {1, 2, 3, 4}; + auto span = AnySpan<T>(v).first(2); + EXPECT_THAT(span, ElementsAre(1, 2)); +} + +TYPED_TEST(AnySpanTest, VectorLast) { + using T = typename TestFixture::template MaybeConst<int>; + std::vector<int> v = {1, 2, 3, 4}; + auto span = AnySpan<T>(v).last(2); + EXPECT_THAT(span, ElementsAre(3, 4)); + span = AnySpan<T>(v).last(4); + EXPECT_THAT(span, ElementsAre(1, 2, 3, 4)); + + std::vector<int> zero_length_vector; + auto zero_length_subspan = AnySpan<T>(zero_length_vector).last(0); + EXPECT_EQ(zero_length_subspan.size(), 0); +} + +TYPED_TEST(AnySpanTest, DequeLast) { + using T = typename TestFixture::template MaybeConst<int>; + std::deque<int> v = {1, 2, 3, 4}; + auto span = AnySpan<T>(v).last(2); + EXPECT_THAT(span, ElementsAre(3, 4)); +} + +TYPED_TEST(AnySpanTest, LambdaTransformFromVector) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::vector<std::string> v = {"b", "a", "d", "c"}; + std::vector<int> alphabetical_order = {1, 0, 3, 2}; + auto subscript_v = [&v](size_t i) -> std::string& { return v[i]; }; + AnySpan<T> span(alphabetical_order, subscript_v); + EXPECT_EQ(span[0], "a"); + EXPECT_EQ(span[1], "b"); + EXPECT_EQ(span[2], "c"); + EXPECT_EQ(span[3], "d"); +} + +TYPED_TEST(AnySpanTest, LambdaTransformFromSpanOfDifferentType) { + using T = typename TestFixture::template MaybeConst<int>; + using U = typename TestFixture::template MaybeConst<std::string>; + std::vector<std::string> v = {"b", "a", "d", "c"}; + std::vector<int> alphabetical_order = {1, 0, 3, 2}; + AnySpan<T> alphabetical_order_as_span(alphabetical_order); + auto subscript_v = [&v](size_t i) -> std::string& { return v[i]; }; + AnySpan<U> span(alphabetical_order_as_span, subscript_v); + EXPECT_EQ(span[0], "a"); + EXPECT_EQ(span[1], "b"); + EXPECT_EQ(span[2], "c"); + EXPECT_EQ(span[3], "d"); +} + +TYPED_TEST(AnySpanTest, LambdaTransformFromSpanOfSameType) { + using T = typename TestFixture::template MaybeConst<int>; + std::vector<int> v = {2, 1, 4, 3}; + std::vector<int> sorted_order = {1, 0, 3, 2}; + AnySpan<T> sorted_order_as_span(sorted_order); + auto subscript_v = [&v](size_t i) -> int& { return v[i]; }; + AnySpan<T> span(sorted_order_as_span, subscript_v); + EXPECT_EQ(span[0], 1); + EXPECT_EQ(span[1], 2); + EXPECT_EQ(span[2], 3); + EXPECT_EQ(span[3], 4); +} + +TYPED_TEST(AnySpanTest, MakeAnySpanFromVector) { + using V = typename TestFixture::template MaybeConst<std::vector<int>>; + using T = typename TestFixture::template MaybeConst<int>; + V v = {1, 2, 3, 4}; + auto span = MakeAnySpan(v); + EXPECT_TRUE((std::is_same<decltype(span), AnySpan<T>>())); + EXPECT_EQ(span.size(), 4); + for (size_t i = 0; i < 4; ++i) { + EXPECT_EQ(&span[i], &v[i]); + } +} + +TYPED_TEST(AnySpanTest, MakeAnySpanFromPtrAndSize) { + using V = typename TestFixture::template MaybeConst<std::vector<int>>; + using T = typename TestFixture::template MaybeConst<int>; + V v = {1, 2, 3, 4}; + auto span = MakeAnySpan(v.data(), v.size()); + EXPECT_TRUE((std::is_same<decltype(span), AnySpan<T>>())); + EXPECT_EQ(span.size(), 4); + for (int i = 0; i < 4; ++i) { + EXPECT_EQ(&span[i], &v[i]); + } +} + +TYPED_TEST(AnySpanTest, MakeAnySpanFromArray) { + using T = typename TestFixture::template MaybeConst<int>; + T a[] = {1, 2, 3, 4}; + auto span = MakeAnySpan(a); + EXPECT_TRUE((std::is_same<decltype(span), AnySpan<T>>())); + EXPECT_EQ(span.size(), 4); + for (int i = 0; i < 4; ++i) { + EXPECT_EQ(&span[i], &a[i]); + } +} + +TYPED_TEST(AnySpanTest, MakeDerefAnySpanFromArray) { + using T = typename TestFixture::template MaybeConst<int>; + T v = 0; + T* a[] = {&v, &v, &v}; + auto span = MakeDerefAnySpan(a); + EXPECT_TRUE((std::is_same<decltype(span), AnySpan<T>>())); + EXPECT_EQ(span.size(), 3); + for (int i = 0; i < 3; ++i) { + EXPECT_EQ(&span[i], a[i]); + } +} + +// Tests transforms that are supported by std::invoke. +TYPED_TEST(AnySpanTest, InvokableTransforms) { + using T = typename TestFixture::template MaybeConst<std::string>; + struct S; + using SRef = typename TestFixture::template MaybeConst<S>&; + struct S { + std::string a, b; + static T& Fun(SRef p) { return p.a; } + std::string& MemFun() { return a; } + const std::string& MemFun() const { return a; } + }; + + // For const span tests, we need a pointer to constant member function. We + // choose the correct type of member function here. + using MemFunPtr = + typename std::conditional<TestFixture::IsConstTest::value, + T& (S::*)() const, T& (S::*)()>::type; + + std::vector<S> v = {{"1", "2"}, {"3", "4"}}; + S a[] = {{"1", "2"}, {"3", "4"}}; + T& (*fun_ptr)(SRef) = &S::Fun; + MemFunPtr mem_fun_ptr = &S::MemFun; + T S::* mem_ptr = &S::a; + + AnySpan<T> spans[] = { + // Function reference. + AnySpan<T>(v, S::Fun), + AnySpan<T>(a, S::Fun), + AnySpan<T>(a, 2, S::Fun), + + // Function pointer prvalue. + AnySpan<T>(v, &S::Fun), + AnySpan<T>(a, &S::Fun), + AnySpan<T>(a, 2, &S::Fun), + + // Function pointer lvalue. + AnySpan<T>(v, fun_ptr), + AnySpan<T>(a, fun_ptr), + AnySpan<T>(a, 2, fun_ptr), + + // Dereferenced function pointer variable. + AnySpan<T>(v, *fun_ptr), + AnySpan<T>(a, *fun_ptr), + AnySpan<T>(a, 2, *fun_ptr), + + // Member function pointer. + AnySpan<T>(v, mem_fun_ptr), + AnySpan<T>(a, mem_fun_ptr), + AnySpan<T>(a, 2, mem_fun_ptr), + + // Data member. + AnySpan<T>(v, mem_ptr), + AnySpan<T>(a, mem_ptr), + AnySpan<T>(a, 2, mem_ptr), + }; + + for (const auto& span : spans) { + EXPECT_EQ(span[0], "1"); + EXPECT_EQ(span[1], "3"); + } +} + +TYPED_TEST(AnySpanTest, VectorPtrSubspan) { + using T = typename TestFixture::template MaybeConst<int>; + std::vector<int> v = {1, 2, 3, 4}; + std::vector<int*> vp; + for (int& i : v) { + vp.push_back(&i); + } + AnySpan<T> span = AnySpan<T>(vp, any_span_transform::Deref()).subspan(1, 2); + EXPECT_EQ(span.size(), 2); + EXPECT_EQ(span[0], 2); + EXPECT_EQ(span[1], 3); +} + +TYPED_TEST(AnySpanTest, VectorPtrFirst) { + using T = typename TestFixture::template MaybeConst<int>; + std::vector<int> v = {1, 2, 3, 4}; + std::vector<int*> vp; + for (int& i : v) { + vp.push_back(&i); + } + AnySpan<T> span = AnySpan<T>(vp, any_span_transform::Deref()).first(2); + EXPECT_THAT(span, ElementsAre(1, 2)); +} + +TYPED_TEST(AnySpanTest, VectorPtrLast) { + using T = typename TestFixture::template MaybeConst<int>; + std::vector<int> v = {1, 2, 3, 4}; + std::vector<int*> vp; + for (int& i : v) { + vp.push_back(&i); + } + AnySpan<T> span = AnySpan<T>(vp, any_span_transform::Deref()).last(2); + EXPECT_THAT(span, ElementsAre(3, 4)); +} + +TYPED_TEST(AnySpanTest, Iterator) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::vector<std::string> v = {"0", "1", "2", "3"}; + AnySpan<T> span(v); + + // Dereference. + auto it = span.begin(); + EXPECT_EQ("0", *it); + + // Postfix increment. + EXPECT_EQ("0", *(it++)); + EXPECT_EQ("1", *(it++)); + + // Postfix decrement. + EXPECT_EQ("2", *(it--)); + EXPECT_EQ("1", *(it--)); + + // Prefix increment. + EXPECT_EQ(it, span.begin()); + EXPECT_EQ("1", *(++it)); + EXPECT_EQ("2", *(++it)); + + // Postfix decrement. + EXPECT_EQ("1", *(--it)); + EXPECT_EQ("0", *(--it)); + + // Increment by integer. + EXPECT_EQ(it, span.begin()); + it += 3; + EXPECT_EQ("3", *it); + + // Decrement by integer. + it = span.end(); + it -= 4; + EXPECT_EQ("0", *it); + + // Add and subtract. + EXPECT_EQ(v.begin() + 2, v.end() - 2); + EXPECT_EQ(2 + v.begin(), v.end() - 2); + + // Index. + it = span.begin(); + EXPECT_EQ("3", v[3]); + + // Relational operators. + EXPECT_TRUE(span.begin() != span.end()); + EXPECT_TRUE(span.begin() == span.begin()); + EXPECT_TRUE(span.begin() < span.end()); + EXPECT_TRUE(span.begin() <= span.end()); + EXPECT_TRUE(span.end() > span.begin()); + EXPECT_TRUE(span.end() >= span.begin()); +} + +TYPED_TEST(AnySpanTest, ConstIterator) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::vector<std::string> v = {"0", "1", "2", "3"}; + AnySpan<T> span(v); + + // Dereference. + auto it = span.cbegin(); + EXPECT_EQ("0", *it); + + // Postfix increment. + EXPECT_EQ("0", *(it++)); + EXPECT_EQ("1", *(it++)); + + // Postfix decrement. + EXPECT_EQ("2", *(it--)); + EXPECT_EQ("1", *(it--)); + + // Prefix increment. + EXPECT_EQ(it, span.cbegin()); + EXPECT_EQ("1", *(++it)); + EXPECT_EQ("2", *(++it)); + + // Postfix decrement. + EXPECT_EQ("1", *(--it)); + EXPECT_EQ("0", *(--it)); + + // Increment by integer. + EXPECT_EQ(it, span.cbegin()); + it += 3; + EXPECT_EQ("3", *it); + + // Decrement by integer. + it = span.cend(); + it -= 4; + EXPECT_EQ("0", *it); + + // Add and subtract. + EXPECT_EQ(v.cbegin() + 2, v.cend() - 2); + EXPECT_EQ(2 + v.cbegin(), v.cend() - 2); + + // Index. + it = span.cbegin(); + EXPECT_EQ("3", v[3]); + + // Relational operators. + EXPECT_TRUE(span.cbegin() != span.cend()); + EXPECT_TRUE(span.cbegin() == span.cbegin()); + EXPECT_TRUE(span.cbegin() < span.cend()); + EXPECT_TRUE(span.cbegin() <= span.cend()); + EXPECT_TRUE(span.cend() > span.cbegin()); + EXPECT_TRUE(span.cend() >= span.cbegin()); +} + +TYPED_TEST(AnySpanTest, ReverseIterator) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::vector<std::string> v = {"a", "b", "c", "d"}; + AnySpan<T> span(v); + auto v_it = v.rbegin(); + for (auto it = span.rbegin(); it != span.rend(); ++it, ++v_it) { + EXPECT_EQ(*v_it, *it); + } +} + +TYPED_TEST(AnySpanTest, IndexingAt) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::vector<std::string> v = {"a", "b", "c", "d"}; + AnySpan<T> span(v); + EXPECT_EQ(&v.at(0), &v.at(0)); + EXPECT_EQ(&v.at(1), &v.at(1)); + EXPECT_EQ(&v.at(3), &v.at(3)); +} + +TYPED_TEST(AnySpanTest, AtThrows) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::vector<std::string> v = {"a", "b", "c", "d"}; + AnySpan<T> span(v); + ABSL_BASE_INTERNAL_EXPECT_FAIL(span.at(4), std::out_of_range, + "failed bounds check"); +} + +TYPED_TEST(AnySpanTest, FrontBack) { + using T = typename TestFixture::template MaybeConst<std::string>; + std::vector<std::string> v = {"a", "b", "c", "d"}; + AnySpan<T> span(v); + EXPECT_EQ(&v.front(), &span.front()); + EXPECT_EQ("a", span.front()); + EXPECT_EQ(&v.back(), &span.back()); + EXPECT_EQ("d", span.back()); +} + +// Returns a reference to a default constructed instance of the given type. +template <typename Container> +Container& DefaultInstance() { + static auto* c = new Container; + return *c; +} + +// This test reaches into the implementation a bit, but it buys a lot of +// confidence that the flat-array optimizations are working correctly. +TYPED_TEST(AnySpanTest, CheapTypes) { + using T = typename TestFixture::template MaybeConst<int>; + using any_span_internal::IsCheap; + EXPECT_FALSE(IsCheap(AnySpan<T>(DefaultInstance<std::deque<int>>()))); + EXPECT_FALSE( + IsCheap(AnySpan<T>(DefaultInstance<std::vector<std::unique_ptr<int>>>(), + any_span_transform::Deref()))); + EXPECT_TRUE(IsCheap(AnySpan<T>(DefaultInstance<std::vector<int>>()))); + EXPECT_TRUE(IsCheap(AnySpan<T>(DefaultInstance<absl::Span<int>>()))); + EXPECT_TRUE(IsCheap(AnySpan<T>(DefaultInstance<std::vector<int>>()))); + EXPECT_TRUE(IsCheap(AnySpan<T>(DefaultInstance<std::vector<int*>>(), + any_span_transform::Deref()))); + EXPECT_TRUE(IsCheap(AnySpan<T>(DefaultInstance<std::vector<int*>>(), + any_span_transform::Deref()))); + struct SomeContainer { + int& operator[](std::size_t) { ABSL_RAW_LOG(FATAL, "Not implemented"); } + const int& operator[](std::size_t) const { + ABSL_RAW_LOG(FATAL, "Not implemented"); + } + std::size_t size() const { return 1; } + }; + EXPECT_FALSE(IsCheap(AnySpan<T>(DefaultInstance<SomeContainer>()))); + struct SomeCheapMutableContainer { + int& operator[](std::size_t) const { + ABSL_RAW_LOG(FATAL, "Not implemented"); + } + int* data() const { return nullptr; } + std::size_t size() const { return 1; } + }; + EXPECT_TRUE( + IsCheap(AnySpan<T>(DefaultInstance<SomeCheapMutableContainer>()))); + + struct SomeWonkyContainer { + int& operator[](std::size_t) const { + ABSL_RAW_LOG(FATAL, "Not implemented"); + } + const int** data() const { return nullptr; } + std::size_t size() const { return 1; } + }; + EXPECT_FALSE(IsCheap(AnySpan<T>(DefaultInstance<SomeWonkyContainer>()))); +} + +TYPED_TEST(AnySpanTest, TriviallyCopyable) { + using T = typename TestFixture::template MaybeConst<std::string>; + // Note: we could use is_trivially_copyable, but it is not implemented in + // Crosstool v18 which is the current version at the time of writing. + EXPECT_TRUE(std::is_trivially_copy_constructible<AnySpan<T>>::value); + EXPECT_TRUE(std::is_trivially_copy_assignable<AnySpan<T>>::value); + EXPECT_TRUE(std::is_trivially_destructible<AnySpan<T>>::value); +} + +TYPED_TEST(AnySpanTest, IsConstructible) { + using T = typename TestFixture::template MaybeConst<std::string>; + using U = typename TestFixture::template MaybeConst<int>; + using VectorT = + typename TestFixture::template MaybeConst<std::vector<std::string>>; + using VectorU = typename TestFixture::template MaybeConst<std::vector<int>>; + EXPECT_FALSE((std::is_constructible<AnySpan<T>, T>::value)); + EXPECT_FALSE((std::is_constructible<AnySpan<T>, T*>::value)); + EXPECT_FALSE((std::is_constructible<AnySpan<T>, U>::value)); + EXPECT_FALSE((std::is_constructible<AnySpan<T>, U(&)[5]>::value)); + EXPECT_FALSE((std::is_constructible<AnySpan<T>, U*, std::size_t>::value)); + EXPECT_FALSE((std::is_constructible<AnySpan<T>, VectorU&>::value)); + + EXPECT_TRUE((std::is_constructible<AnySpan<T>, VectorT&>::value)); + EXPECT_TRUE((std::is_constructible<AnySpan<T>, T(&)[5]>::value)); + EXPECT_TRUE((std::is_constructible<AnySpan<T>, T*, std::size_t>::value)); +} + +// +// ConstAnySpanTest covers parts of AnySpan interface that are only valid for +// AnySpans with const elements. +// + +TEST(ConstAnySpanTest, ImplicitConversion) { + std::vector<int> v = {1, 2, 3}; + ExpectSpanEqualsContainer<const int>(v, v); +} + +TEST(ConstAnySpanTest, TemporaryArrayOfKnownSize) { + const int i = 5; + EXPECT_THAT(AnySpan<const int>({&i, &i, &i}, any_span_transform::Deref()), + testing::ElementsAre(5, 5, 5)); +} + +TEST(ConstAnySpanTest, CheapTypes) { + using any_span_internal::IsCheap; + EXPECT_TRUE(IsCheap(AnySpan<const int>(std::vector<const int*>(), + any_span_transform::Deref()))); + EXPECT_TRUE(IsCheap(AnySpan<const int>(std::vector<const int*>(), + any_span_transform::Deref()))); + EXPECT_TRUE( + IsCheap(AnySpan<const int>(DefaultInstance<absl::Span<const int>>()))); + struct SomeCheapConstContainer { + const int& operator[](std::size_t) const { + ABSL_RAW_LOG(FATAL, "Not implemented"); + } + const int* data() const { return nullptr; } + std::size_t size() const { return 1; } + }; + EXPECT_TRUE(IsCheap(AnySpan<const int>(SomeCheapConstContainer()))); +} + +TEST(ConstAnySpanTest, InitializerListInt) { + auto test = [](AnySpan<const int> span) { + int expected = 1; + for (int i : span) { + EXPECT_EQ(expected++, i); + } + EXPECT_EQ(expected, 5); + }; + test({1, 2, 3, 4}); +} + +TEST(ConstAnySpanTest, InitializerListString) { + auto test = [](AnySpan<const std::string> span) { + int expected = 1; + for (const std::string& i : span) { + EXPECT_EQ(absl::StrCat(expected++), i); + } + EXPECT_EQ(expected, 5); + }; + test({"1", "2", "3", "4"}); +} + +TEST(ConstAnySpanTest, InitializerListDerivedToBase) { + // Derived to base, implicit conversion. + [](AnySpan<const Base2> bases) { + for (const Base2& b : bases) { + EXPECT_EQ(b.b2, 2); + // Make sure the value was not sliced. + EXPECT_EQ(typeid(b), typeid(Child)); + } + }({Child{}, Child{}, Child{}}); + + // Derived to base with explicit transform. + Child children[3]; + [](AnySpan<const Base2> bases) { + for (const Base2& b : bases) { + EXPECT_EQ(b.b2, 2); + // Make sure the value was not sliced. + EXPECT_EQ(typeid(b), typeid(Child)); + } + }({{Child{}, Child{}, Child{}}, + [](auto& obj) -> const Base2& { return obj; }}); +} + +TEST(ConstAnySpanTest, + InitializerListUsesValueTypeWhenConversionNeedsTemporaries) { + // If this were to call the initializer_list<Element> overload it would fail + // when it tries to make an lvalue transform from std::string to + // std::string_view. + // Instead, it must be calling the initializer_list<value_type> overload, + // doing the conversion on the call site. + [](AnySpan<const std::string_view> s) { + EXPECT_THAT(s, ElementsAre("A", "B", "C")); + }({"A", "B", "C"}); +} + +TEST(ConstAnySpanTest, MakeAnySpanFromVector) { + std::vector<int> v = {1, 2, 3, 4}; + auto span = MakeConstAnySpan(v); + EXPECT_TRUE((std::is_same<decltype(span), AnySpan<const int>>())); + EXPECT_EQ(span.size(), 4); + for (size_t i = 0; i < 4; ++i) { + EXPECT_EQ(&span[i], &v[i]); + } +} + +TEST(ConstAnySpanTest, MakeAnySpanFromPtrAndSize) { + std::vector<int> v = {1, 2, 3, 4}; + auto span = MakeConstAnySpan(v.data(), v.size()); + EXPECT_TRUE((std::is_same<decltype(span), AnySpan<const int>>())); + EXPECT_EQ(span.size(), 4); + for (size_t i = 0; i < 4; ++i) { + EXPECT_EQ(&span[i], &v[i]); + } +} + +TEST(ConstAnySpanTest, MakeAnySpanFromArray) { + int a[] = {1, 2, 3, 4}; + auto span = MakeConstAnySpan(a); + EXPECT_TRUE((std::is_same<decltype(span), AnySpan<const int>>())); + EXPECT_EQ(span.size(), 4); + for (size_t i = 0; i < 4; ++i) { + EXPECT_EQ(&span[i], &a[i]); + } +} + +TEST(ConstAnySpanTest, MakeDerefAnySpanFromArray) { + int v = 0; + int* a[] = {&v, &v, &v}; + auto span = MakeConstDerefAnySpan(a); + EXPECT_TRUE((std::is_same<decltype(span), AnySpan<const int>>())); + EXPECT_EQ(span.size(), 3); + for (size_t i = 0; i < 3; ++i) { + EXPECT_EQ(&span[i], a[i]); + } +} + +// +// MutableAnySpanTest covers parts of AnySpan interface that are only valid for +// AnySpans with mutable elements. +// + +TEST(MutableAnySpanTest, MutableVectorString) { + std::vector<std::string> v = {"a", "b", "c", "d"}; + AnySpan<std::string> span(v); + span[0] = "e"; + EXPECT_EQ(v[0], "e"); +} + +TEST(MutableAnySpanTest, MutableToConstConversion) { + std::vector<std::string> v = {"a", "b", "c", "d"}; + AnySpan<std::string> mutable_span(v); + AnySpan<const std::string> const_span(mutable_span); + EXPECT_TRUE(any_span_internal::IsCheap(const_span)); + mutable_span[0] = "e"; + EXPECT_EQ(const_span[0], "e"); + mutable_span.subspan(1)[0] = "f"; + EXPECT_EQ(const_span[1], "f"); + + std::vector<std::string> v2 = {"a"}; + mutable_span = AnySpan<std::string>(v2); + EXPECT_EQ(const_span[0], "e"); +} + +TEST(MutableAnySpanTest, DerefMutableToConstConversion) { + std::vector<std::string> v = {"a", "b", "c", "d"}; + std::vector<std::string*> v_ptrs; + for (std::string& s : v) { + v_ptrs.push_back(&s); + } + AnySpan<std::string> mutable_span(v_ptrs, any_span_transform::Deref()); + AnySpan<const std::string> const_span(mutable_span); + EXPECT_TRUE(any_span_internal::IsCheap(const_span)); + mutable_span[0] = "e"; + EXPECT_EQ(const_span[0], "e"); + EXPECT_EQ(v[0], "e"); + mutable_span.subspan(1)[0] = "f"; + EXPECT_EQ(const_span[1], "f"); +} + +TEST(MutableAnySpanTest, MutableToConstDeque) { + std::deque<std::string> d = {"a", "b", "c", "d"}; + AnySpan<std::string> mutable_span(d); + AnySpan<const std::string> const_span(mutable_span); + EXPECT_FALSE(any_span_internal::IsCheap(const_span)); + mutable_span[0] = "e"; + EXPECT_EQ(const_span[0], "e"); + mutable_span.subspan(1)[0] = "f"; + EXPECT_EQ(const_span[1], "f"); +} + +TEST(MutableAnySpanTest, Deref) { + std::string s = "a"; + AnySpan<std::string>({&s}, any_span_transform::Deref())[0] = "b"; + EXPECT_EQ(s, "b"); +} + +// Checks that recommended usage works. +TEST(MutableAnySpanTest, AsFunctionArgument) { + std::vector<std::string> v = {"a", "b", "c", "d"}; + auto set_all_to_e = [](AnySpan<std::string> span) { + for (std::string& s : span) { + s = "e"; + } + }; + set_all_to_e(AnySpan<std::string>(v)); + for (const std::string& e : v) { + EXPECT_EQ(e, "e"); + } +} + +TEST(MutableAnySpanTest, MutateThroughIterator) { + std::vector<std::string> v = {"a", "b", "c", "d"}; + AnySpan<std::string> span(v); + AnySpan<std::string>::iterator it = span.begin(); + it[2] = "e"; + EXPECT_EQ(v[2], "e"); + ++it; + *it = "f"; + EXPECT_EQ(v[1], "f"); + for (std::string& s : span) { + s = "g"; + } + for (const std::string& e : v) { + EXPECT_EQ(e, "g"); + } +} + +TEST(MutableAnySpanTest, MutateThroughSubSpan) { + std::vector<int> v = {1, 2, 3, 4}; + AnySpan<int> span(v); + span = span.subspan(1, 2); + for (int& i : span) { + i = 42; + } + EXPECT_THAT(v, ElementsAre(1, 42, 42, 4)); +} + +TYPED_TEST(AnySpanTest, SubspanToEnd) { + int arr[] = {0, 1, 2}; + AnySpan<int> span(arr); + EXPECT_THAT(span.subspan(0, AnySpan<int>::npos), ElementsAre(0, 1, 2)); + EXPECT_THAT(span.subspan(1, AnySpan<int>::npos), ElementsAre(1, 2)); + EXPECT_THAT(span.subspan(2, AnySpan<int>::npos), ElementsAre(2)); + EXPECT_THAT(span.subspan(3, AnySpan<int>::npos), ElementsAre()); +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(span.subspan(4, AnySpan<int>::npos), ""); + EXPECT_DEATH(span.subspan(AnySpan<int>::npos, AnySpan<int>::npos), ""); + } +#endif +} + +TEST(MutableAnySpanTest, NonTruncatingSubspan) { + std::vector<int> v = {1, 2, 3, 4}; + AnySpan<int> span(v); +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(span.subspan(5, 0), ""); + EXPECT_DEATH(span.subspan(5, 1), ""); + EXPECT_DEATH(span.subspan(AnySpan<int>::npos, 0), ""); + EXPECT_DEATH(span.subspan(AnySpan<int>::npos, 1), ""); + EXPECT_DEATH(span.subspan(0, 5), ""); + EXPECT_DEATH(span.first(5), ""); + EXPECT_DEATH(span.first(AnySpan<int>::npos), ""); + } +#endif +} + +TEST(MutableAnySpanTest, IteratorToConstIteratorConversion) { + std::vector<int> v = {1}; + AnySpan<int> span(v); + AnySpan<int>::iterator it = span.begin(); + AnySpan<int>::const_iterator cit = it; + EXPECT_EQ(&*it, &*cit); +} + +// Checks that implicit conversion from a mutable view works. +TEST(MutableAnySpanTest, ImplicitConversionFromMutableSpan) { + std::vector<std::string> v = {"a", "b", "c", "d"}; + absl::Span<std::string> mutable_slice(v); + auto set_all_to_e = [](AnySpan<std::string> span) { + for (std::string& s : span) { + s = "e"; + } + }; + set_all_to_e(mutable_slice); + for (const std::string& s : v) { + EXPECT_EQ(s, "e"); + } +} + +bool WantsStringOrAnySpan(absl::string_view /* ignored */) { return false; } +bool WantsStringOrAnySpan(AnySpan<std::string> /* ignored */) { return true; } +bool WantsStringOrConstAnySpan(absl::string_view /* ignored */) { + return false; +} +bool WantsStringOrConstAnySpan(AnySpan<const std::string> /* ignored */) { + return true; +} +// Checks that AnySpan(char (&array)[N]) is not used in the overload +// resolution when building an AnySpan<string> (it would not compile anyway). +TEST(MutableAnySpanTest, ResolveAmbiguityWithAnySpanOfChars) { + std::string foo; + AnySpan<std::string> bar; + EXPECT_FALSE(WantsStringOrAnySpan("abc")); + EXPECT_FALSE(WantsStringOrAnySpan(foo)); + EXPECT_TRUE(WantsStringOrAnySpan(bar)); + EXPECT_FALSE(WantsStringOrConstAnySpan("abc")); + EXPECT_FALSE(WantsStringOrConstAnySpan(foo)); + EXPECT_TRUE(WantsStringOrConstAnySpan(bar)); +} + +TEST(AnySpanTest, SupportsAbslHash) { + std::vector<size_t> v = {1, 2, 3}; + std::vector<std::string> vs = {"1", "2", "3"}; + std::vector<std::unique_ptr<size_t>> vu; + vu.push_back(std::make_unique<size_t>(1)); + vu.push_back(std::make_unique<size_t>(2)); + vu.push_back(std::make_unique<size_t>(3)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({ + // {1, 2, 3} + AnySpan<const size_t>(v), + AnySpan<const size_t>(vu, any_span_transform::Deref()), + // {1, 2} + AnySpan<const size_t>(std::vector<size_t>({1, 2})), + AnySpan<const size_t>(std::deque<size_t>({1, 2})), + // {1, 2, 4} + AnySpan<const size_t>(std::vector<size_t>({1, 2, 4})), + AnySpan<const size_t>(std::deque<size_t>({1, 2, 4})), + // Empty. + AnySpan<const size_t>(), + AnySpan<const size_t>(std::vector<size_t>()), + })); +} + +// This is necessary to avoid a bunch of lint warnings suggesting that we use +// EXPECT_EQ/etc. +bool Identity(bool b) { return b; } + +template <typename T, typename U, typename V> +void TestEquality(T&& orig, U&& eq, V&& diff) { + EXPECT_TRUE(Identity(std::forward<T>(orig) == std::forward<U>(eq))); + EXPECT_TRUE(Identity(std::forward<U>(eq) == std::forward<T>(orig))); + EXPECT_FALSE(Identity(std::forward<T>(orig) != std::forward<U>(eq))); + EXPECT_FALSE(Identity(std::forward<U>(eq) != std::forward<T>(orig))); + + EXPECT_FALSE(Identity(std::forward<T>(orig) == std::forward<V>(diff))); + EXPECT_FALSE(Identity(std::forward<V>(diff) == std::forward<T>(orig))); + EXPECT_TRUE(Identity(std::forward<T>(orig) != std::forward<V>(diff))); + EXPECT_TRUE(Identity(std::forward<V>(diff) != std::forward<T>(orig))); +} + +TEST(AnySpanTest, Equals) { + int arr[] = {1, 2, 3}; + std::vector<int> eq(std::begin(arr), std::end(arr)); + int diff[] = {1, 2, 4}; + TestEquality(AnySpan<int>(arr), AnySpan<int>(eq), AnySpan<int>(diff)); + TestEquality(AnySpan<const int>(arr), AnySpan<int>(eq), AnySpan<int>(diff)); + TestEquality(AnySpan<int>(arr), AnySpan<const int>(eq), + AnySpan<const int>(diff)); + TestEquality(AnySpan<const int>(arr), AnySpan<const int>(eq), + AnySpan<const int>(diff)); + + TestEquality(AnySpan<const int>(arr), eq, diff); + TestEquality(AnySpan<int>(arr), eq, diff); +} + +// If an object is copied, mark the bool pointed by `copied` true. +class MarkIfCopied { + public: + explicit MarkIfCopied(bool* copied) : copied_(copied) {} + + // NOLINT_NEXT_LINE(google-explicit-constructor) + MarkIfCopied(const MarkIfCopied& other) { *this = other; } + + MarkIfCopied& operator=(const MarkIfCopied& other) { + copied_ = other.copied_; + *copied_ = true; + return *this; + } + + private: + bool* copied_; +}; + +TEST(MarkIfCopied, EnsureWorks) { + bool copied = false; + MarkIfCopied item(&copied); + ASSERT_FALSE(copied); + MarkIfCopied _unused_copied_item(item); + EXPECT_TRUE(copied); +} + +TEST(AnySpanTest, ReferenceWrapperNotCopied) { + bool copied1 = false; + bool copied2 = false; + MarkIfCopied item1(&copied1); + MarkIfCopied item2(&copied2); + std::vector<std::reference_wrapper<const MarkIfCopied>> items; + items.push_back(std::cref(item1)); + items.push_back(std::cref(item2)); + AnySpan<const MarkIfCopied> span(items); + EXPECT_EQ(2, span.size()); + EXPECT_FALSE(copied1); + EXPECT_FALSE(copied2); +} + +} // namespace +ABSL_NAMESPACE_END +} // namespace absl
diff --git a/absl/types/compare.h b/absl/types/compare.h index edf1039..abc8616 100644 --- a/absl/types/compare.h +++ b/absl/types/compare.h
@@ -449,7 +449,7 @@ // or three-way comparator. // SFINAE prevents implicit conversions to bool (such as from int). template <typename BoolT, - absl::enable_if_t<std::is_same<bool, BoolT>::value, int> = 0> + std::enable_if_t<std::is_same<bool, BoolT>::value, int> = 0> constexpr bool compare_result_as_less_than(const BoolT r) { return r; } @@ -467,7 +467,7 @@ // three-way comparator. // SFINAE prevents implicit conversions to int (such as from bool). template <typename Int, - absl::enable_if_t<std::is_same<int, Int>::value, int> = 0> + std::enable_if_t<std::is_same<int, Int>::value, int> = 0> constexpr absl::weak_ordering compare_result_as_ordering(const Int c) { return c < 0 ? absl::weak_ordering::less : c == 0 ? absl::weak_ordering::equivalent @@ -479,7 +479,7 @@ } template <typename Compare, typename K, typename LK, - absl::enable_if_t< + std::enable_if_t< !std::is_same< bool, absl::result_of_t<Compare(const K&, const LK&)>>::value, int> = 0> @@ -488,7 +488,7 @@ return compare_result_as_ordering(compare(x, y)); } template <typename Compare, typename K, typename LK, - absl::enable_if_t< + std::enable_if_t< std::is_same< bool, absl::result_of_t<Compare(const K&, const LK&)>>::value, int> = 0>
diff --git a/absl/types/internal/any_span.h b/absl/types/internal/any_span.h new file mode 100644 index 0000000..50843bf --- /dev/null +++ b/absl/types/internal/any_span.h
@@ -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 = std::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<std::remove_const_t<T>>& other) { + using MutableT = std::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_
diff --git a/absl/types/internal/span.h b/absl/types/internal/span.h index 9e8e418..8448cd7 100644 --- a/absl/types/internal/span.h +++ b/absl/types/internal/span.h
@@ -54,7 +54,7 @@ // Detection idioms for size() and data(). template <typename C> using HasSize = - std::is_integral<absl::decay_t<decltype(std::declval<C&>().size())>>; + std::is_integral<std::decay_t<decltype(std::declval<C&>().size())>>; // We want to enable conversion from vector<T*> to Span<const T* const> but // disable conversion from vector<Derived> to Span<Base>. Here we use @@ -64,13 +64,13 @@ // which returns a reference. template <typename T, typename C> using HasData = - std::is_convertible<absl::decay_t<decltype(GetData(std::declval<C&>()))>*, + std::is_convertible<std::decay_t<decltype(GetData(std::declval<C&>()))>*, T* const*>; // Extracts value type from a Container template <typename C> struct ElementType { - using type = typename absl::remove_reference_t<C>::value_type; + using type = typename std::remove_reference_t<C>::value_type; }; template <typename T, size_t N>
diff --git a/absl/types/source_location.cc b/absl/types/source_location.cc new file mode 100644 index 0000000..47028a5 --- /dev/null +++ b/absl/types/source_location.cc
@@ -0,0 +1,18 @@ +// 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 "absl/types/source_location.h" + +#include "absl/base/attributes.h" +#include "absl/base/config.h"
diff --git a/absl/types/span.h b/absl/types/span.h index b9f6b74..8c5736c 100644 --- a/absl/types/span.h +++ b/absl/types/span.h
@@ -204,7 +204,7 @@ public: using element_type = T; - using value_type = absl::remove_cv_t<T>; + using value_type = std::remove_cv_t<T>; // TODO(b/316099902) - pointer should be absl_nullable, but this makes it hard // to recognize foreach loops as safe. absl_nullability_unknown is currently // used to suppress -Wnullability-completeness warnings.