Export of internal Abseil changes
--
990253454819ce26ff1dda9ab4bbc145b61d01e4 by Xiaoyi Zhang <zhangxy@google.com>:
Import github PR https://github.com/abseil/abseil-cpp/pull/645
PiperOrigin-RevId: 303119797
--
5ac845cb7929b7d1eaf59a309afd811db5001175 by Abseil Team <absl-team@google.com>:
Fix internal exception spec compatibility error
PiperOrigin-RevId: 303104081
--
3290595dd866eecab3c7044e2e3ca0adb74f1bf5 by Gennadiy Rozental <rogeeff@google.com>:
Use FlagValue<T> to represent the value of a flag. Place it directly after
FlagImpl and use a computed offset refer to it.
The offset is computed based on the assumption that the `value_` data member
is placed directly after the impl_ data member in Flag<T>.
This change will allow us to migrate to `T`-specific storage in the generic case.
This change decreases the overhead for int flags by 32 bytes.
PiperOrigin-RevId: 303038099
--
f2b37722cd7a6d3a60ef9713f0d2bbff56f3ddbf by Derek Mauro <dmauro@google.com>:
Minor correctness fix for an ABSL_HAVE_BUILTIN conditional
PiperOrigin-RevId: 302980666
--
39c079a6141ae1c5728af8bf33a39c8aff9deb9f by Abseil Team <absl-team@google.com>:
Use ABSL_HARDENING_ASSERT in b-tree and SwissTable iterators.
PiperOrigin-RevId: 302970075
--
9668a044e080c789df32bcaa1ffb5100831cd9fa by Benjamin Barenblat <bbaren@google.com>:
Correct `add_subdirectory` line in CMake googletest support
Commit bcefbdcdf6 added support for building with CMake against a local googletest checkout, but I missed a line when constructing the diff. Change the `add_subdirectory` line to reference the correct directories.
PiperOrigin-RevId: 302947488
--
0a3c10fabf80a43ca69ab8b1570030e55f2be741 by Andy Soffer <asoffer@google.com>:
Remove unused distribution format traits.
PiperOrigin-RevId: 302896176
--
0478f2f6270e5ed64c0e28ec09556ca90b2d46a9 by Samuel Benzaquen <sbenza@google.com>:
Fix for CWG:2310.
PiperOrigin-RevId: 302734089
--
3cb978dda5cae5905affdc0914dcc2d27671ed11 by Samuel Benzaquen <sbenza@google.com>:
Fix the Allocate/Deallocate functions to use the same underlying allocator type.
PiperOrigin-RevId: 302721804
--
ae38d3984fb68b4e3ddc165fa8d5c24d5936be52 by Matthew Brown <matthewbr@google.com>:
Internal Change
PiperOrigin-RevId: 302717314
--
7357cf7abd03cc60b6e82b5f28a8e34935c3b4dc by Andy Getzendanner <durandal@google.com>:
Fix typo: s/ABSL_HARDENED_ASSERT/ABSL_HARDENING_ASSERT/
PiperOrigin-RevId: 302532164
GitOrigin-RevId: 990253454819ce26ff1dda9ab4bbc145b61d01e4
Change-Id: Ie595a221c16e1e7e1255ad42e029b646c5f3e11d
			
			
This commit is contained in:
		
							parent
							
								
									132d791b40
								
							
						
					
					
						commit
						79e0dc1151
					
				
					 29 changed files with 387 additions and 607 deletions
				
			
		|  | @ -38,6 +38,4 @@ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) | |||
| 
 | ||||
| # Add googletest directly to our build. This defines the gtest and gtest_main | ||||
| # targets. | ||||
| add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src | ||||
|                  ${CMAKE_BINARY_DIR}/googletest-build | ||||
|                  EXCLUDE_FROM_ALL) | ||||
| add_subdirectory(${absl_gtest_src_dir} ${absl_gtest_build_dir} EXCLUDE_FROM_ALL) | ||||
|  |  | |||
|  | @ -212,7 +212,8 @@ ABSL_NAMESPACE_END | |||
| // 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.
 | ||||
| #if ABSL_HAVE_BUILTIN(__builtin_trap) || \ | ||||
| #if (ABSL_HAVE_BUILTIN(__builtin_trap) &&         \ | ||||
|      ABSL_HAVE_BUILTIN(__builtin_unreachable)) || \ | ||||
|     (defined(__GNUC__) && !defined(__clang__)) | ||||
| #define ABSL_INTERNAL_HARDENING_ABORT() \ | ||||
|   do {                                  \ | ||||
|  | @ -225,11 +226,11 @@ ABSL_NAMESPACE_END | |||
| 
 | ||||
| // ABSL_HARDENING_ASSERT()
 | ||||
| //
 | ||||
| // `ABSL_HARDENED_ASSERT()` is like `ABSL_ASSERT()`, but used to implement
 | ||||
| // `ABSL_HARDENING_ASSERT()` is like `ABSL_ASSERT()`, but used to implement
 | ||||
| // runtime assertions that should be enabled in hardened builds even when
 | ||||
| // `NDEBUG` is defined.
 | ||||
| //
 | ||||
| // When `NDEBUG` is not defined, `ABSL_HARDENED_ASSERT()` is identical to
 | ||||
| // When `NDEBUG` is not defined, `ABSL_HARDENING_ASSERT()` is identical to
 | ||||
| // `ABSL_ASSERT()`.
 | ||||
| //
 | ||||
| // See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on
 | ||||
|  |  | |||
|  | @ -937,8 +937,13 @@ struct btree_iterator { | |||
|   } | ||||
| 
 | ||||
|   // Accessors for the key/value the iterator is pointing at.
 | ||||
|   reference operator*() const { return node->value(position); } | ||||
|   pointer operator->() const { return &node->value(position); } | ||||
|   reference operator*() const { | ||||
|     ABSL_HARDENING_ASSERT(node != nullptr); | ||||
|     ABSL_HARDENING_ASSERT(node->start() <= position); | ||||
|     ABSL_HARDENING_ASSERT(node->finish() > position); | ||||
|     return node->value(position); | ||||
|   } | ||||
|   pointer operator->() const { return &operator*(); } | ||||
| 
 | ||||
|   btree_iterator &operator++() { | ||||
|     increment(); | ||||
|  | @ -1769,6 +1774,7 @@ void btree_iterator<N, R, P>::increment_slow() { | |||
|       position = node->position(); | ||||
|       node = node->parent(); | ||||
|     } | ||||
|     // TODO(ezb): assert we aren't incrementing end() instead of handling.
 | ||||
|     if (position == node->finish()) { | ||||
|       *this = save; | ||||
|     } | ||||
|  | @ -1792,6 +1798,7 @@ void btree_iterator<N, R, P>::decrement_slow() { | |||
|       position = node->position() - 1; | ||||
|       node = node->parent(); | ||||
|     } | ||||
|     // TODO(ezb): assert we aren't decrementing begin() instead of handling.
 | ||||
|     if (position < node->start()) { | ||||
|       *this = save; | ||||
|     } | ||||
|  |  | |||
|  | @ -138,6 +138,7 @@ class node_handle<Policy, PolicyTraits, Alloc, | |||
|                   absl::void_t<typename Policy::mapped_type>> | ||||
|     : public node_handle_base<PolicyTraits, Alloc> { | ||||
|   using Base = node_handle_base<PolicyTraits, Alloc>; | ||||
|   using slot_type = typename PolicyTraits::slot_type; | ||||
| 
 | ||||
|  public: | ||||
|   using key_type = typename Policy::key_type; | ||||
|  | @ -145,7 +146,7 @@ class node_handle<Policy, PolicyTraits, Alloc, | |||
| 
 | ||||
|   constexpr node_handle() {} | ||||
| 
 | ||||
|   auto key() const -> decltype(PolicyTraits::key(this->slot())) { | ||||
|   auto key() const -> decltype(PolicyTraits::key(std::declval<slot_type*>())) { | ||||
|     return PolicyTraits::key(this->slot()); | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -37,6 +37,9 @@ namespace absl { | |||
| ABSL_NAMESPACE_BEGIN | ||||
| namespace container_internal { | ||||
| 
 | ||||
| template <size_t Alignment> | ||||
| struct alignas(Alignment) AlignedType {}; | ||||
| 
 | ||||
| // Allocates at least n bytes aligned to the specified alignment.
 | ||||
| // Alignment must be a power of 2. It must be positive.
 | ||||
| //
 | ||||
|  | @ -48,7 +51,7 @@ template <size_t Alignment, class Alloc> | |||
| void* Allocate(Alloc* alloc, size_t n) { | ||||
|   static_assert(Alignment > 0, ""); | ||||
|   assert(n && "n must be positive"); | ||||
|   struct alignas(Alignment) M {}; | ||||
|   using M = AlignedType<Alignment>; | ||||
|   using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; | ||||
|   using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; | ||||
|   A mem_alloc(*alloc); | ||||
|  | @ -64,7 +67,7 @@ template <size_t Alignment, class Alloc> | |||
| void Deallocate(Alloc* alloc, void* p, size_t n) { | ||||
|   static_assert(Alignment > 0, ""); | ||||
|   assert(n && "n must be positive"); | ||||
|   struct alignas(Alignment) M {}; | ||||
|   using M = AlignedType<Alignment>; | ||||
|   using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; | ||||
|   using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; | ||||
|   A mem_alloc(*alloc); | ||||
|  |  | |||
|  | @ -16,6 +16,8 @@ | |||
| 
 | ||||
| #include <cstdint> | ||||
| #include <tuple> | ||||
| #include <typeindex> | ||||
| #include <typeinfo> | ||||
| #include <utility> | ||||
| 
 | ||||
| #include "gmock/gmock.h" | ||||
|  | @ -27,6 +29,9 @@ ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal { | ||||
| namespace { | ||||
| 
 | ||||
| using ::testing::Gt; | ||||
| using ::testing::_; | ||||
| using ::testing::ElementsAre; | ||||
| using ::testing::Pair; | ||||
| 
 | ||||
| TEST(Memory, AlignmentLargerThanBase) { | ||||
|  | @ -45,6 +50,39 @@ TEST(Memory, AlignmentSmallerThanBase) { | |||
|   Deallocate<2>(&alloc, mem, 3); | ||||
| } | ||||
| 
 | ||||
| std::map<std::type_index, int>& AllocationMap() { | ||||
|   static auto* map = new std::map<std::type_index, int>; | ||||
|   return *map; | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| struct TypeCountingAllocator { | ||||
|   TypeCountingAllocator() = default; | ||||
|   template <typename U> | ||||
|   TypeCountingAllocator(const TypeCountingAllocator<U>&) {}  // NOLINT
 | ||||
| 
 | ||||
|   using value_type = T; | ||||
| 
 | ||||
|   T* allocate(size_t n, const void* = nullptr) { | ||||
|     AllocationMap()[typeid(T)] += n; | ||||
|     return std::allocator<T>().allocate(n); | ||||
|   } | ||||
|   void deallocate(T* p, std::size_t n) { | ||||
|     AllocationMap()[typeid(T)] -= n; | ||||
|     return std::allocator<T>().deallocate(p, n); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| TEST(Memory, AllocateDeallocateMatchType) { | ||||
|   TypeCountingAllocator<int> alloc; | ||||
|   void* mem = Allocate<1>(&alloc, 1); | ||||
|   // Verify that it was allocated
 | ||||
|   EXPECT_THAT(AllocationMap(), ElementsAre(Pair(_, Gt(0)))); | ||||
|   Deallocate<1>(&alloc, mem, 1); | ||||
|   // Verify that the deallocation matched.
 | ||||
|   EXPECT_THAT(AllocationMap(), ElementsAre(Pair(_, 0))); | ||||
| } | ||||
| 
 | ||||
| class Fixture : public ::testing::Test { | ||||
|   using Alloc = std::allocator<std::string>; | ||||
| 
 | ||||
|  |  | |||
|  | @ -104,6 +104,7 @@ | |||
| 
 | ||||
| #include "absl/base/internal/bits.h" | ||||
| #include "absl/base/internal/endian.h" | ||||
| #include "absl/base/macros.h" | ||||
| #include "absl/base/port.h" | ||||
| #include "absl/container/internal/common.h" | ||||
| #include "absl/container/internal/compressed_tuple.h" | ||||
|  | @ -651,9 +652,9 @@ class raw_hash_set { | |||
|     iterator(ctrl_t* ctrl) : ctrl_(ctrl) {}  // for end()
 | ||||
|     iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {} | ||||
| 
 | ||||
|     void assert_is_full() const { assert(IsFull(*ctrl_)); } | ||||
|     void assert_is_full() const { ABSL_HARDENING_ASSERT(IsFull(*ctrl_)); } | ||||
|     void assert_is_valid() const { | ||||
|       assert(!ctrl_ || IsFull(*ctrl_) || *ctrl_ == kSentinel); | ||||
|       ABSL_HARDENING_ASSERT(!ctrl_ || IsFull(*ctrl_) || *ctrl_ == kSentinel); | ||||
|     } | ||||
| 
 | ||||
|     void skip_empty_or_deleted() { | ||||
|  |  | |||
|  | @ -91,30 +91,30 @@ struct S2 { | |||
| }; | ||||
| 
 | ||||
| TEST_F(FlagTest, Traits) { | ||||
|   EXPECT_EQ(flags::FlagValue::Kind<int>(), | ||||
|   EXPECT_EQ(flags::StorageKind<int>(), | ||||
|             flags::FlagValueStorageKind::kOneWordAtomic); | ||||
|   EXPECT_EQ(flags::FlagValue::Kind<bool>(), | ||||
|   EXPECT_EQ(flags::StorageKind<bool>(), | ||||
|             flags::FlagValueStorageKind::kOneWordAtomic); | ||||
|   EXPECT_EQ(flags::FlagValue::Kind<double>(), | ||||
|   EXPECT_EQ(flags::StorageKind<double>(), | ||||
|             flags::FlagValueStorageKind::kOneWordAtomic); | ||||
|   EXPECT_EQ(flags::FlagValue::Kind<int64_t>(), | ||||
|   EXPECT_EQ(flags::StorageKind<int64_t>(), | ||||
|             flags::FlagValueStorageKind::kOneWordAtomic); | ||||
| 
 | ||||
| #if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) | ||||
|   EXPECT_EQ(flags::FlagValue::Kind<S1>(), | ||||
|   EXPECT_EQ(flags::StorageKind<S1>(), | ||||
|             flags::FlagValueStorageKind::kTwoWordsAtomic); | ||||
|   EXPECT_EQ(flags::FlagValue::Kind<S2>(), | ||||
|   EXPECT_EQ(flags::StorageKind<S2>(), | ||||
|             flags::FlagValueStorageKind::kTwoWordsAtomic); | ||||
| #else | ||||
|   EXPECT_EQ(flags::FlagValue::Kind<S1>(), | ||||
|   EXPECT_EQ(flags::StorageKind<S1>(), | ||||
|             flags::FlagValueStorageKind::kHeapAllocated); | ||||
|   EXPECT_EQ(flags::FlagValue::Kind<S2>(), | ||||
|   EXPECT_EQ(flags::StorageKind<S2>(), | ||||
|             flags::FlagValueStorageKind::kHeapAllocated); | ||||
| #endif | ||||
| 
 | ||||
|   EXPECT_EQ(flags::FlagValue::Kind<std::string>(), | ||||
|   EXPECT_EQ(flags::StorageKind<std::string>(), | ||||
|             flags::FlagValueStorageKind::kHeapAllocated); | ||||
|   EXPECT_EQ(flags::FlagValue::Kind<std::vector<std::string>>(), | ||||
|   EXPECT_EQ(flags::StorageKind<std::vector<std::string>>(), | ||||
|             flags::FlagValueStorageKind::kHeapAllocated); | ||||
| } | ||||
| 
 | ||||
|  | @ -624,10 +624,10 @@ TEST_F(FlagTest, TestNonDefaultConstructibleType) { | |||
|   EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 25); | ||||
| } | ||||
| 
 | ||||
| // --------------------------------------------------------------------
 | ||||
| 
 | ||||
| }  // namespace
 | ||||
| 
 | ||||
| // --------------------------------------------------------------------
 | ||||
| 
 | ||||
| ABSL_RETIRED_FLAG(bool, old_bool_flag, true, "old descr"); | ||||
| ABSL_RETIRED_FLAG(int, old_int_flag, (int)std::sqrt(10), "old descr"); | ||||
| ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr")); | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ | |||
| #include <vector> | ||||
| 
 | ||||
| #include "absl/base/attributes.h" | ||||
| #include "absl/base/casts.h" | ||||
| #include "absl/base/config.h" | ||||
| #include "absl/base/const_init.h" | ||||
| #include "absl/base/optimization.h" | ||||
|  | @ -135,18 +136,18 @@ void FlagImpl::Init() { | |||
|       (*default_value_.gen_func)(), DynValueDeleter{op_}); | ||||
|   switch (ValueStorageKind()) { | ||||
|     case FlagValueStorageKind::kHeapAllocated: | ||||
|       value_.dynamic = init_value.release(); | ||||
|       HeapAllocatedValue() = init_value.release(); | ||||
|       break; | ||||
|     case FlagValueStorageKind::kOneWordAtomic: { | ||||
|       int64_t atomic_value; | ||||
|       std::memcpy(&atomic_value, init_value.get(), flags_internal::Sizeof(op_)); | ||||
|       value_.one_word_atomic.store(atomic_value, std::memory_order_release); | ||||
|       std::memcpy(&atomic_value, init_value.get(), Sizeof(op_)); | ||||
|       OneWordValue().store(atomic_value, std::memory_order_release); | ||||
|       break; | ||||
|     } | ||||
|     case FlagValueStorageKind::kTwoWordsAtomic: { | ||||
|       AlignedTwoWords atomic_value{0, 0}; | ||||
|       std::memcpy(&atomic_value, init_value.get(), flags_internal::Sizeof(op_)); | ||||
|       value_.two_words_atomic.store(atomic_value, std::memory_order_release); | ||||
|       std::memcpy(&atomic_value, init_value.get(), Sizeof(op_)); | ||||
|       TwoWordsValue().store(atomic_value, std::memory_order_release); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|  | @ -198,18 +199,18 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const { | |||
| void FlagImpl::StoreValue(const void* src) { | ||||
|   switch (ValueStorageKind()) { | ||||
|     case FlagValueStorageKind::kHeapAllocated: | ||||
|       flags_internal::Copy(op_, src, value_.dynamic); | ||||
|       Copy(op_, src, HeapAllocatedValue()); | ||||
|       break; | ||||
|     case FlagValueStorageKind::kOneWordAtomic: { | ||||
|       int64_t one_word_val; | ||||
|       std::memcpy(&one_word_val, src, flags_internal::Sizeof(op_)); | ||||
|       value_.one_word_atomic.store(one_word_val, std::memory_order_release); | ||||
|       int64_t one_word_val = 0; | ||||
|       std::memcpy(&one_word_val, src, Sizeof(op_)); | ||||
|       OneWordValue().store(one_word_val, std::memory_order_release); | ||||
|       break; | ||||
|     } | ||||
|     case FlagValueStorageKind::kTwoWordsAtomic: { | ||||
|       AlignedTwoWords two_words_val{0, 0}; | ||||
|       std::memcpy(&two_words_val, src, flags_internal::Sizeof(op_)); | ||||
|       value_.two_words_atomic.store(two_words_val, std::memory_order_release); | ||||
|       std::memcpy(&two_words_val, src, Sizeof(op_)); | ||||
|       TwoWordsValue().store(two_words_val, std::memory_order_release); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|  | @ -258,17 +259,19 @@ std::string FlagImpl::CurrentValue() const { | |||
|   switch (ValueStorageKind()) { | ||||
|     case FlagValueStorageKind::kHeapAllocated: { | ||||
|       absl::MutexLock l(guard); | ||||
|       return flags_internal::Unparse(op_, value_.dynamic); | ||||
|       return flags_internal::Unparse(op_, HeapAllocatedValue()); | ||||
|     } | ||||
|     case FlagValueStorageKind::kOneWordAtomic: { | ||||
|       const auto one_word_val = | ||||
|           value_.one_word_atomic.load(std::memory_order_acquire); | ||||
|       return flags_internal::Unparse(op_, &one_word_val); | ||||
|           absl::bit_cast<std::array<char, sizeof(int64_t)>>( | ||||
|               OneWordValue().load(std::memory_order_acquire)); | ||||
|       return flags_internal::Unparse(op_, one_word_val.data()); | ||||
|     } | ||||
|     case FlagValueStorageKind::kTwoWordsAtomic: { | ||||
|       const auto two_words_val = | ||||
|           value_.two_words_atomic.load(std::memory_order_acquire); | ||||
|       return flags_internal::Unparse(op_, &two_words_val); | ||||
|           absl::bit_cast<std::array<char, sizeof(AlignedTwoWords)>>( | ||||
|               TwoWordsValue().load(std::memory_order_acquire)); | ||||
|       return flags_internal::Unparse(op_, two_words_val.data()); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | @ -317,18 +320,18 @@ std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() { | |||
|   switch (ValueStorageKind()) { | ||||
|     case FlagValueStorageKind::kHeapAllocated: { | ||||
|       return absl::make_unique<FlagState>( | ||||
|           this, flags_internal::Clone(op_, value_.dynamic), modified, | ||||
|           this, flags_internal::Clone(op_, HeapAllocatedValue()), modified, | ||||
|           on_command_line, counter_); | ||||
|     } | ||||
|     case FlagValueStorageKind::kOneWordAtomic: { | ||||
|       return absl::make_unique<FlagState>( | ||||
|           this, value_.one_word_atomic.load(std::memory_order_acquire), | ||||
|           modified, on_command_line, counter_); | ||||
|           this, OneWordValue().load(std::memory_order_acquire), modified, | ||||
|           on_command_line, counter_); | ||||
|     } | ||||
|     case FlagValueStorageKind::kTwoWordsAtomic: { | ||||
|       return absl::make_unique<FlagState>( | ||||
|           this, value_.two_words_atomic.load(std::memory_order_acquire), | ||||
|           modified, on_command_line, counter_); | ||||
|           this, TwoWordsValue().load(std::memory_order_acquire), modified, | ||||
|           on_command_line, counter_); | ||||
|     } | ||||
|   } | ||||
|   return nullptr; | ||||
|  | @ -359,6 +362,28 @@ bool FlagImpl::RestoreState(const FlagState& flag_state) { | |||
|   return true; | ||||
| } | ||||
| 
 | ||||
| template <typename StorageT> | ||||
| typename StorageT::value_type& FlagImpl::OffsetValue() const { | ||||
|   char* p = reinterpret_cast<char*>(const_cast<FlagImpl*>(this)); | ||||
|   // The offset is deduced via Flag value type specific op_.
 | ||||
|   size_t offset = flags_internal::ValueOffset(op_); | ||||
| 
 | ||||
|   return reinterpret_cast<StorageT*>(p + offset)->value; | ||||
| } | ||||
| 
 | ||||
| void*& FlagImpl::HeapAllocatedValue() const { | ||||
|   assert(ValueStorageKind() == FlagValueStorageKind::kHeapAllocated); | ||||
|   return OffsetValue<FlagHeapAllocatedValue>(); | ||||
| } | ||||
| std::atomic<int64_t>& FlagImpl::OneWordValue() const { | ||||
|   assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic); | ||||
|   return OffsetValue<FlagOneWordValue>(); | ||||
| } | ||||
| std::atomic<AlignedTwoWords>& FlagImpl::TwoWordsValue() const { | ||||
|   assert(ValueStorageKind() == FlagValueStorageKind::kTwoWordsAtomic); | ||||
|   return OffsetValue<FlagTwoWordsValue>(); | ||||
| } | ||||
| 
 | ||||
| // Attempts to parse supplied `value` string using parsing routine in the `flag`
 | ||||
| // argument. If parsing successful, this function replaces the dst with newly
 | ||||
| // parsed value. In case if any error is encountered in either step, the error
 | ||||
|  | @ -383,20 +408,19 @@ void FlagImpl::Read(void* dst) const { | |||
|   switch (ValueStorageKind()) { | ||||
|     case FlagValueStorageKind::kHeapAllocated: { | ||||
|       absl::MutexLock l(guard); | ||||
| 
 | ||||
|       flags_internal::CopyConstruct(op_, value_.dynamic, dst); | ||||
|       flags_internal::CopyConstruct(op_, HeapAllocatedValue(), dst); | ||||
|       break; | ||||
|     } | ||||
|     case FlagValueStorageKind::kOneWordAtomic: { | ||||
|       const auto one_word_val = | ||||
|           value_.one_word_atomic.load(std::memory_order_acquire); | ||||
|       std::memcpy(dst, &one_word_val, flags_internal::Sizeof(op_)); | ||||
|       const int64_t one_word_val = | ||||
|           OneWordValue().load(std::memory_order_acquire); | ||||
|       std::memcpy(dst, &one_word_val, Sizeof(op_)); | ||||
|       break; | ||||
|     } | ||||
|     case FlagValueStorageKind::kTwoWordsAtomic: { | ||||
|       const auto two_words_val = | ||||
|           value_.two_words_atomic.load(std::memory_order_acquire); | ||||
|       std::memcpy(dst, &two_words_val, flags_internal::Sizeof(op_)); | ||||
|       const AlignedTwoWords two_words_val = | ||||
|           TwoWordsValue().load(std::memory_order_acquire); | ||||
|       std::memcpy(dst, &two_words_val, Sizeof(op_)); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|  |  | |||
|  | @ -53,56 +53,13 @@ enum class FlagOp { | |||
|   kStaticTypeId, | ||||
|   kParse, | ||||
|   kUnparse, | ||||
|   kValueOffset, | ||||
| }; | ||||
| using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*); | ||||
| 
 | ||||
| // Flag value specific operations routine.
 | ||||
| // Forward declaration for Flag value specific operations.
 | ||||
| template <typename T> | ||||
| void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { | ||||
|   switch (op) { | ||||
|     case FlagOp::kDelete: | ||||
|       delete static_cast<const T*>(v1); | ||||
|       return nullptr; | ||||
|     case FlagOp::kClone: | ||||
|       return new T(*static_cast<const T*>(v1)); | ||||
|     case FlagOp::kCopy: | ||||
|       *static_cast<T*>(v2) = *static_cast<const T*>(v1); | ||||
|       return nullptr; | ||||
|     case FlagOp::kCopyConstruct: | ||||
|       new (v2) T(*static_cast<const T*>(v1)); | ||||
|       return nullptr; | ||||
|     case FlagOp::kSizeof: | ||||
|       return reinterpret_cast<void*>(sizeof(T)); | ||||
|     case FlagOp::kStaticTypeId: { | ||||
|       auto* static_id = &FlagStaticTypeIdGen<T>; | ||||
| 
 | ||||
|       // Cast from function pointer to void* is not portable.
 | ||||
|       // We don't have an easy way to work around this, but it works fine
 | ||||
|       // on all the platforms we test and as long as size of pointers match
 | ||||
|       // we should be fine to do reinterpret cast.
 | ||||
|       static_assert(sizeof(void*) == sizeof(static_id), | ||||
|                     "Flag's static type id does not work on this platform"); | ||||
|       return reinterpret_cast<void*>(static_id); | ||||
|     } | ||||
|     case FlagOp::kParse: { | ||||
|       // Initialize the temporary instance of type T based on current value in
 | ||||
|       // destination (which is going to be flag's default value).
 | ||||
|       T temp(*static_cast<T*>(v2)); | ||||
|       if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp, | ||||
|                               static_cast<std::string*>(v3))) { | ||||
|         return nullptr; | ||||
|       } | ||||
|       *static_cast<T*>(v2) = std::move(temp); | ||||
|       return v2; | ||||
|     } | ||||
|     case FlagOp::kUnparse: | ||||
|       *static_cast<std::string*>(v2) = | ||||
|           absl::UnparseFlag<T>(*static_cast<const T*>(v1)); | ||||
|       return nullptr; | ||||
|     default: | ||||
|       return nullptr; | ||||
|   } | ||||
| } | ||||
| void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3); | ||||
| 
 | ||||
| // Deletes memory interpreting obj as flag value type pointer.
 | ||||
| inline void Delete(FlagOpFn op, const void* obj) { | ||||
|  | @ -144,6 +101,16 @@ inline FlagStaticTypeId StaticTypeId(FlagOpFn op) { | |||
|   return reinterpret_cast<FlagStaticTypeId>( | ||||
|       op(FlagOp::kStaticTypeId, nullptr, nullptr, nullptr)); | ||||
| } | ||||
| // Returns offset of the field value_ from the field impl_ inside of
 | ||||
| // absl::Flag<T> data. Given FlagImpl pointer p you can get the
 | ||||
| // location of the corresponding value as:
 | ||||
| //      reinterpret_cast<char*>(p) + ValueOffset().
 | ||||
| inline ptrdiff_t ValueOffset(FlagOpFn op) { | ||||
|   // This sequence of casts reverses the sequence from
 | ||||
|   // `flags_internal::FlagOps()`
 | ||||
|   return static_cast<ptrdiff_t>(reinterpret_cast<intptr_t>( | ||||
|       op(FlagOp::kValueOffset, nullptr, nullptr, nullptr))); | ||||
| } | ||||
| 
 | ||||
| ///////////////////////////////////////////////////////////////////////////////
 | ||||
| // Flag help auxiliary structs.
 | ||||
|  | @ -239,6 +206,10 @@ using FlagUseOneWordStorage = std::integral_constant< | |||
| struct alignas(16) AlignedTwoWords { | ||||
|   int64_t first; | ||||
|   int64_t second; | ||||
| 
 | ||||
|   bool IsInitialized() const { | ||||
|     return first != flags_internal::UninitializedFlagValue(); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
|  | @ -248,8 +219,14 @@ using FlagUseTwoWordsStorage = std::integral_constant< | |||
| #else | ||||
| // This is actually unused and only here to avoid ifdefs in other palces.
 | ||||
| struct AlignedTwoWords { | ||||
|   constexpr AlignedTwoWords() = default; | ||||
|   constexpr AlignedTwoWords(int64_t, int64_t) {} | ||||
|   constexpr AlignedTwoWords() noexcept : dummy() {} | ||||
|   constexpr AlignedTwoWords(int64_t, int64_t) noexcept : dummy() {} | ||||
|   char dummy; | ||||
| 
 | ||||
|   bool IsInitialized() const { | ||||
|     std::abort(); | ||||
|     return true; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| // This trait should be type dependent, otherwise SFINAE below will fail
 | ||||
|  | @ -269,23 +246,70 @@ enum class FlagValueStorageKind : uint8_t { | |||
|   kTwoWordsAtomic = 2 | ||||
| }; | ||||
| 
 | ||||
| union FlagValue { | ||||
|   constexpr explicit FlagValue(int64_t v) : one_word_atomic(v) {} | ||||
| template <typename T> | ||||
| static constexpr FlagValueStorageKind StorageKind() { | ||||
|   return FlagUseHeapStorage<T>::value | ||||
|              ? FlagValueStorageKind::kHeapAllocated | ||||
|              : FlagUseOneWordStorage<T>::value | ||||
|                    ? FlagValueStorageKind::kOneWordAtomic | ||||
|                    : FlagUseTwoWordsStorage<T>::value | ||||
|                          ? FlagValueStorageKind::kTwoWordsAtomic | ||||
|                          : FlagValueStorageKind::kHeapAllocated; | ||||
| } | ||||
| 
 | ||||
|   template <typename T> | ||||
|   static constexpr FlagValueStorageKind Kind() { | ||||
|     return FlagUseHeapStorage<T>::value | ||||
|                ? FlagValueStorageKind::kHeapAllocated | ||||
|                : FlagUseOneWordStorage<T>::value | ||||
|                      ? FlagValueStorageKind::kOneWordAtomic | ||||
|                      : FlagUseTwoWordsStorage<T>::value | ||||
|                            ? FlagValueStorageKind::kTwoWordsAtomic | ||||
|                            : FlagValueStorageKind::kHeapAllocated; | ||||
| struct FlagHeapAllocatedValue { | ||||
|   using value_type = void*; | ||||
| 
 | ||||
|   value_type value; | ||||
| }; | ||||
| 
 | ||||
| struct FlagOneWordValue { | ||||
|   using value_type = std::atomic<int64_t>; | ||||
|   constexpr FlagOneWordValue() : value(UninitializedFlagValue()) {} | ||||
| 
 | ||||
|   value_type value; | ||||
| }; | ||||
| 
 | ||||
| struct FlagTwoWordsValue { | ||||
|   using value_type = std::atomic<AlignedTwoWords>; | ||||
|   constexpr FlagTwoWordsValue() | ||||
|       : value(AlignedTwoWords{UninitializedFlagValue(), 0}) {} | ||||
| 
 | ||||
|   value_type value; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, | ||||
|           FlagValueStorageKind Kind = flags_internal::StorageKind<T>()> | ||||
| struct FlagValue; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct FlagValue<T, FlagValueStorageKind::kHeapAllocated> | ||||
|     : FlagHeapAllocatedValue { | ||||
|   bool Get(T*) const { return false; } | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct FlagValue<T, FlagValueStorageKind::kOneWordAtomic> : FlagOneWordValue { | ||||
|   bool Get(T* dst) const { | ||||
|     int64_t one_word_val = value.load(std::memory_order_acquire); | ||||
|     if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) { | ||||
|       return false; | ||||
|     } | ||||
|     std::memcpy(dst, static_cast<const void*>(&one_word_val), sizeof(T)); | ||||
|     return true; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
|   void* dynamic; | ||||
|   std::atomic<int64_t> one_word_atomic; | ||||
|   std::atomic<flags_internal::AlignedTwoWords> two_words_atomic; | ||||
| template <typename T> | ||||
| struct FlagValue<T, FlagValueStorageKind::kTwoWordsAtomic> : FlagTwoWordsValue { | ||||
|   bool Get(T* dst) const { | ||||
|     AlignedTwoWords two_words_val = value.load(std::memory_order_acquire); | ||||
|     if (ABSL_PREDICT_FALSE(!two_words_val.IsInitialized())) { | ||||
|       return false; | ||||
|     } | ||||
|     std::memcpy(dst, static_cast<const void*>(&two_words_val), sizeof(T)); | ||||
|     return true; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| ///////////////////////////////////////////////////////////////////////////////
 | ||||
|  | @ -333,35 +357,10 @@ class FlagImpl final : public flags_internal::CommandLineFlag { | |||
|         counter_(0), | ||||
|         callback_(nullptr), | ||||
|         default_value_(default_value_gen), | ||||
|         value_(flags_internal::UninitializedFlagValue()), | ||||
|         data_guard_{} {} | ||||
| 
 | ||||
|   // Constant access methods
 | ||||
|   void Read(void* dst) const override ABSL_LOCKS_EXCLUDED(*DataGuard()); | ||||
|   template <typename T, typename std::enable_if<FlagUseHeapStorage<T>::value, | ||||
|                                                 int>::type = 0> | ||||
|   void Get(T* dst) const { | ||||
|     Read(dst); | ||||
|   } | ||||
|   template <typename T, typename std::enable_if<FlagUseOneWordStorage<T>::value, | ||||
|                                                 int>::type = 0> | ||||
|   void Get(T* dst) const { | ||||
|     int64_t one_word_val = | ||||
|         value_.one_word_atomic.load(std::memory_order_acquire); | ||||
|     if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) { | ||||
|       DataGuard();  // Make sure flag initialized
 | ||||
|       one_word_val = value_.one_word_atomic.load(std::memory_order_acquire); | ||||
|     } | ||||
|     std::memcpy(dst, static_cast<const void*>(&one_word_val), sizeof(T)); | ||||
|   } | ||||
|   template <typename T, typename std::enable_if< | ||||
|                             FlagUseTwoWordsStorage<T>::value, int>::type = 0> | ||||
|   void Get(T* dst) const { | ||||
|     DataGuard();  // Make sure flag initialized
 | ||||
|     const auto two_words_val = | ||||
|         value_.two_words_atomic.load(std::memory_order_acquire); | ||||
|     std::memcpy(dst, &two_words_val, sizeof(T)); | ||||
|   } | ||||
| 
 | ||||
|   // Mutating access methods
 | ||||
|   void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard()); | ||||
|  | @ -391,6 +390,25 @@ class FlagImpl final : public flags_internal::CommandLineFlag { | |||
|       ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); | ||||
|   // Flag initialization called via absl::call_once.
 | ||||
|   void Init(); | ||||
| 
 | ||||
|   // Offset value access methods. One per storage kind. These methods to not
 | ||||
|   // respect const correctness, so be very carefull using them.
 | ||||
| 
 | ||||
|   // This is a shared helper routine which encapsulates most of the magic. Since
 | ||||
|   // it is only used inside the three routines below, which are defined in
 | ||||
|   // flag.cc, we can define it in that file as well.
 | ||||
|   template <typename StorageT> | ||||
|   typename StorageT::value_type& OffsetValue() const; | ||||
|   // This is an accessor for a value stored in heap allocated storage.
 | ||||
|   // Returns a mutable reference to a pointer to allow vlaue mutation.
 | ||||
|   void*& HeapAllocatedValue() const; | ||||
|   // This is an accessor for a value stored as one word atomic. Returns a
 | ||||
|   // mutable reference to an atomic value.
 | ||||
|   std::atomic<int64_t>& OneWordValue() const; | ||||
|   // This is an accessor for a value stored as two words atomic. Returns a
 | ||||
|   // mutable reference to an atomic value.
 | ||||
|   std::atomic<AlignedTwoWords>& TwoWordsValue() const; | ||||
| 
 | ||||
|   // Attempts to parse supplied `value` string. If parsing is successful,
 | ||||
|   // returns new value. Otherwise returns nullptr.
 | ||||
|   std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value, | ||||
|  | @ -488,13 +506,6 @@ class FlagImpl final : public flags_internal::CommandLineFlag { | |||
|   // these two cases.
 | ||||
|   FlagDefaultSrc default_value_; | ||||
| 
 | ||||
|   // Atomically mutable flag's state
 | ||||
| 
 | ||||
|   // Flag's value. This can be either the atomically stored small value or
 | ||||
|   // pointer to the heap allocated dynamic value. value_storage_kind_ is used
 | ||||
|   // to distinguish these cases.
 | ||||
|   FlagValue value_; | ||||
| 
 | ||||
|   // This is reserved space for an absl::Mutex to guard flag data. It will be
 | ||||
|   // initialized in FlagImpl::Init via placement new.
 | ||||
|   // We can't use "absl::Mutex data_guard_", since this class is not literal.
 | ||||
|  | @ -514,8 +525,9 @@ class Flag { | |||
|  public: | ||||
|   constexpr Flag(const char* name, const char* filename, const FlagHelpArg help, | ||||
|                  const FlagDfltGenFunc default_value_gen) | ||||
|       : impl_(name, filename, &FlagOps<T>, help, FlagValue::Kind<T>(), | ||||
|               default_value_gen) {} | ||||
|       : impl_(name, filename, &FlagOps<T>, help, | ||||
|               flags_internal::StorageKind<T>(), default_value_gen), | ||||
|         value_() {} | ||||
| 
 | ||||
|   T Get() const { | ||||
|     // See implementation notes in CommandLineFlag::Get().
 | ||||
|  | @ -530,7 +542,7 @@ class Flag { | |||
|     impl_.AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>); | ||||
| #endif | ||||
| 
 | ||||
|     impl_.Get(&u.value); | ||||
|     if (!value_.Get(&u.value)) impl_.Read(&u.value); | ||||
|     return std::move(u.value); | ||||
|   } | ||||
|   void Set(const T& v) { | ||||
|  | @ -556,10 +568,63 @@ class Flag { | |||
|  private: | ||||
|   template <typename U, bool do_register> | ||||
|   friend class FlagRegistrar; | ||||
| 
 | ||||
|   // Flag's data
 | ||||
|   // The implementation depends on value_ field to be placed exactly after the
 | ||||
|   // impl_ field, so that impl_ can figure out the offset to the value and
 | ||||
|   // access it.
 | ||||
|   FlagImpl impl_; | ||||
|   FlagValue<T> value_; | ||||
| }; | ||||
| 
 | ||||
| ///////////////////////////////////////////////////////////////////////////////
 | ||||
| // Implementation of Flag value specific operations routine.
 | ||||
| template <typename T> | ||||
| void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { | ||||
|   switch (op) { | ||||
|     case FlagOp::kDelete: | ||||
|       delete static_cast<const T*>(v1); | ||||
|       return nullptr; | ||||
|     case FlagOp::kClone: | ||||
|       return new T(*static_cast<const T*>(v1)); | ||||
|     case FlagOp::kCopy: | ||||
|       *static_cast<T*>(v2) = *static_cast<const T*>(v1); | ||||
|       return nullptr; | ||||
|     case FlagOp::kCopyConstruct: | ||||
|       new (v2) T(*static_cast<const T*>(v1)); | ||||
|       return nullptr; | ||||
|     case FlagOp::kSizeof: | ||||
|       return reinterpret_cast<void*>(static_cast<uintptr_t>(sizeof(T))); | ||||
|     case FlagOp::kStaticTypeId: | ||||
|       return reinterpret_cast<void*>(&FlagStaticTypeIdGen<T>); | ||||
|     case FlagOp::kParse: { | ||||
|       // Initialize the temporary instance of type T based on current value in
 | ||||
|       // destination (which is going to be flag's default value).
 | ||||
|       T temp(*static_cast<T*>(v2)); | ||||
|       if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp, | ||||
|                               static_cast<std::string*>(v3))) { | ||||
|         return nullptr; | ||||
|       } | ||||
|       *static_cast<T*>(v2) = std::move(temp); | ||||
|       return v2; | ||||
|     } | ||||
|     case FlagOp::kUnparse: | ||||
|       *static_cast<std::string*>(v2) = | ||||
|           absl::UnparseFlag<T>(*static_cast<const T*>(v1)); | ||||
|       return nullptr; | ||||
|     case FlagOp::kValueOffset: { | ||||
|       // Round sizeof(FlagImp) to a multiple of alignof(FlagValue<T>) to get the
 | ||||
|       // offset of the data.
 | ||||
|       ptrdiff_t round_to = alignof(FlagValue<T>); | ||||
|       ptrdiff_t offset = | ||||
|           (sizeof(FlagImpl) + round_to - 1) / round_to * round_to; | ||||
|       return reinterpret_cast<void*>(offset); | ||||
|     } | ||||
|   } | ||||
|   return nullptr; | ||||
| } | ||||
| 
 | ||||
| ///////////////////////////////////////////////////////////////////////////////
 | ||||
| // This class facilitates Flag object registration and tail expression-based
 | ||||
| // flag definition, for example:
 | ||||
| // ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher);
 | ||||
|  |  | |||
|  | @ -53,7 +53,6 @@ cc_library( | |||
|         "bernoulli_distribution.h", | ||||
|         "beta_distribution.h", | ||||
|         "discrete_distribution.h", | ||||
|         "distribution_format_traits.h", | ||||
|         "distributions.h", | ||||
|         "exponential_distribution.h", | ||||
|         "gaussian_distribution.h", | ||||
|  | @ -141,16 +140,12 @@ cc_library( | |||
| cc_library( | ||||
|     name = "mocking_bit_gen", | ||||
|     testonly = 1, | ||||
|     srcs = [ | ||||
|         "mocking_bit_gen.cc", | ||||
|     ], | ||||
|     hdrs = [ | ||||
|         "mocking_bit_gen.h", | ||||
|     ], | ||||
|     linkopts = ABSL_DEFAULT_LINKOPTS, | ||||
|     deps = [ | ||||
|         ":distributions", | ||||
|         "//absl/base:raw_logging_internal", | ||||
|         "//absl/container:flat_hash_map", | ||||
|         "//absl/meta:type_traits", | ||||
|         "//absl/random/internal:distribution_caller", | ||||
|  |  | |||
|  | @ -102,8 +102,6 @@ absl_cc_library( | |||
|   HDRS | ||||
|     "mock_distributions.h" | ||||
|     "mocking_bit_gen.h" | ||||
|   SRCS | ||||
|     "mocking_bit_gen.cc" | ||||
|   COPTS | ||||
|     ${ABSL_DEFAULT_COPTS} | ||||
|   LINKOPTS | ||||
|  | @ -168,7 +166,6 @@ absl_cc_library( | |||
|     "bernoulli_distribution.h" | ||||
|     "beta_distribution.h" | ||||
|     "discrete_distribution.h" | ||||
|     "distribution_format_traits.h" | ||||
|     "distributions.h" | ||||
|     "exponential_distribution.h" | ||||
|     "gaussian_distribution.h" | ||||
|  |  | |||
|  | @ -132,7 +132,7 @@ namespace random_internal { | |||
| 
 | ||||
| template <> | ||||
| struct DistributionCaller<absl::BitGenRef> { | ||||
|   template <typename DistrT, typename FormatT, typename... Args> | ||||
|   template <typename DistrT, typename... Args> | ||||
|   static typename DistrT::result_type Call(absl::BitGenRef* gen_ref, | ||||
|                                            Args&&... args) { | ||||
|     auto* mock_ptr = gen_ref->mocked_gen_ptr_; | ||||
|  | @ -140,8 +140,7 @@ struct DistributionCaller<absl::BitGenRef> { | |||
|       DistrT dist(std::forward<Args>(args)...); | ||||
|       return dist(*gen_ref); | ||||
|     } else { | ||||
|       return mock_ptr->template Call<DistrT, FormatT>( | ||||
|           std::forward<Args>(args)...); | ||||
|       return mock_ptr->template Call<DistrT>(std::forward<Args>(args)...); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|  |  | |||
|  | @ -1,278 +0,0 @@ | |||
| //
 | ||||
| // Copyright 2018 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_DISTRIBUTION_FORMAT_TRAITS_H_ | ||||
| #define ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_ | ||||
| 
 | ||||
| #include <string> | ||||
| #include <tuple> | ||||
| #include <typeinfo> | ||||
| 
 | ||||
| #include "absl/meta/type_traits.h" | ||||
| #include "absl/random/bernoulli_distribution.h" | ||||
| #include "absl/random/beta_distribution.h" | ||||
| #include "absl/random/exponential_distribution.h" | ||||
| #include "absl/random/gaussian_distribution.h" | ||||
| #include "absl/random/log_uniform_int_distribution.h" | ||||
| #include "absl/random/poisson_distribution.h" | ||||
| #include "absl/random/uniform_int_distribution.h" | ||||
| #include "absl/random/uniform_real_distribution.h" | ||||
| #include "absl/random/zipf_distribution.h" | ||||
| #include "absl/strings/str_cat.h" | ||||
| #include "absl/strings/str_join.h" | ||||
| #include "absl/strings/string_view.h" | ||||
| #include "absl/types/span.h" | ||||
| 
 | ||||
| namespace absl { | ||||
| ABSL_NAMESPACE_BEGIN | ||||
| 
 | ||||
| struct IntervalClosedClosedTag; | ||||
| struct IntervalClosedOpenTag; | ||||
| struct IntervalOpenClosedTag; | ||||
| struct IntervalOpenOpenTag; | ||||
| 
 | ||||
| namespace random_internal { | ||||
| 
 | ||||
| // ScalarTypeName defines a preferred hierarchy of preferred type names for
 | ||||
| // scalars, and is evaluated at compile time for the specific type
 | ||||
| // specialization.
 | ||||
| template <typename T> | ||||
| constexpr const char* ScalarTypeName() { | ||||
|   static_assert(std::is_integral<T>() || std::is_floating_point<T>(), ""); | ||||
|   // clang-format off
 | ||||
|     return | ||||
|         std::is_same<T, float>::value ? "float" : | ||||
|         std::is_same<T, double>::value ? "double" : | ||||
|         std::is_same<T, long double>::value ? "long double" : | ||||
|         std::is_same<T, bool>::value ? "bool" : | ||||
|         std::is_signed<T>::value && sizeof(T) == 1 ? "int8_t" : | ||||
|         std::is_signed<T>::value && sizeof(T) == 2 ? "int16_t" : | ||||
|         std::is_signed<T>::value && sizeof(T) == 4 ? "int32_t" : | ||||
|         std::is_signed<T>::value && sizeof(T) == 8 ? "int64_t" : | ||||
|         std::is_unsigned<T>::value && sizeof(T) == 1 ? "uint8_t" : | ||||
|         std::is_unsigned<T>::value && sizeof(T) == 2 ? "uint16_t" : | ||||
|         std::is_unsigned<T>::value && sizeof(T) == 4 ? "uint32_t" : | ||||
|         std::is_unsigned<T>::value && sizeof(T) == 8 ? "uint64_t" : | ||||
|             "undefined"; | ||||
|   // clang-format on
 | ||||
| 
 | ||||
|   // NOTE: It would be nice to use typeid(T).name(), but that's an
 | ||||
|   // implementation-defined attribute which does not necessarily
 | ||||
|   // correspond to a name. We could potentially demangle it
 | ||||
|   // using, e.g. abi::__cxa_demangle.
 | ||||
| } | ||||
| 
 | ||||
| // Distribution traits used by DistributionCaller and internal implementation
 | ||||
| // details of the mocking framework.
 | ||||
| /*
 | ||||
| struct DistributionFormatTraits { | ||||
|    // Returns the parameterized name of the distribution function.
 | ||||
|    static constexpr const char* FunctionName() | ||||
|    // Format DistrT parameters.
 | ||||
|    static std::string FormatArgs(DistrT& dist); | ||||
|    // Format DistrT::result_type results.
 | ||||
|    static std::string FormatResults(DistrT& dist); | ||||
| }; | ||||
| */ | ||||
| template <typename DistrT> | ||||
| struct DistributionFormatTraits; | ||||
| 
 | ||||
| template <typename R> | ||||
| struct DistributionFormatTraits<absl::uniform_int_distribution<R>> { | ||||
|   using distribution_t = absl::uniform_int_distribution<R>; | ||||
|   using result_t = typename distribution_t::result_type; | ||||
| 
 | ||||
|   static constexpr const char* Name() { return "Uniform"; } | ||||
| 
 | ||||
|   static std::string FunctionName() { | ||||
|     return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); | ||||
|   } | ||||
|   static std::string FormatArgs(const distribution_t& d) { | ||||
|     return absl::StrCat("absl::IntervalClosedClosed, ", (d.min)(), ", ", | ||||
|                         (d.max)()); | ||||
|   } | ||||
|   static std::string FormatResults(absl::Span<const result_t> results) { | ||||
|     return absl::StrJoin(results, ", "); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| template <typename R> | ||||
| struct DistributionFormatTraits<absl::uniform_real_distribution<R>> { | ||||
|   using distribution_t = absl::uniform_real_distribution<R>; | ||||
|   using result_t = typename distribution_t::result_type; | ||||
| 
 | ||||
|   static constexpr const char* Name() { return "Uniform"; } | ||||
| 
 | ||||
|   static std::string FunctionName() { | ||||
|     return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); | ||||
|   } | ||||
|   static std::string FormatArgs(const distribution_t& d) { | ||||
|     return absl::StrCat((d.min)(), ", ", (d.max)()); | ||||
|   } | ||||
|   static std::string FormatResults(absl::Span<const result_t> results) { | ||||
|     return absl::StrJoin(results, ", "); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| template <typename R> | ||||
| struct DistributionFormatTraits<absl::exponential_distribution<R>> { | ||||
|   using distribution_t = absl::exponential_distribution<R>; | ||||
|   using result_t = typename distribution_t::result_type; | ||||
| 
 | ||||
|   static constexpr const char* Name() { return "Exponential"; } | ||||
| 
 | ||||
|   static std::string FunctionName() { | ||||
|     return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); | ||||
|   } | ||||
|   static std::string FormatArgs(const distribution_t& d) { | ||||
|     return absl::StrCat(d.lambda()); | ||||
|   } | ||||
|   static std::string FormatResults(absl::Span<const result_t> results) { | ||||
|     return absl::StrJoin(results, ", "); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| template <typename R> | ||||
| struct DistributionFormatTraits<absl::poisson_distribution<R>> { | ||||
|   using distribution_t = absl::poisson_distribution<R>; | ||||
|   using result_t = typename distribution_t::result_type; | ||||
| 
 | ||||
|   static constexpr const char* Name() { return "Poisson"; } | ||||
| 
 | ||||
|   static std::string FunctionName() { | ||||
|     return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); | ||||
|   } | ||||
|   static std::string FormatArgs(const distribution_t& d) { | ||||
|     return absl::StrCat(d.mean()); | ||||
|   } | ||||
|   static std::string FormatResults(absl::Span<const result_t> results) { | ||||
|     return absl::StrJoin(results, ", "); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| template <> | ||||
| struct DistributionFormatTraits<absl::bernoulli_distribution> { | ||||
|   using distribution_t = absl::bernoulli_distribution; | ||||
|   using result_t = typename distribution_t::result_type; | ||||
| 
 | ||||
|   static constexpr const char* Name() { return "Bernoulli"; } | ||||
| 
 | ||||
|   static constexpr const char* FunctionName() { return Name(); } | ||||
|   static std::string FormatArgs(const distribution_t& d) { | ||||
|     return absl::StrCat(d.p()); | ||||
|   } | ||||
|   static std::string FormatResults(absl::Span<const result_t> results) { | ||||
|     return absl::StrJoin(results, ", "); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| template <typename R> | ||||
| struct DistributionFormatTraits<absl::beta_distribution<R>> { | ||||
|   using distribution_t = absl::beta_distribution<R>; | ||||
|   using result_t = typename distribution_t::result_type; | ||||
| 
 | ||||
|   static constexpr const char* Name() { return "Beta"; } | ||||
| 
 | ||||
|   static std::string FunctionName() { | ||||
|     return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); | ||||
|   } | ||||
|   static std::string FormatArgs(const distribution_t& d) { | ||||
|     return absl::StrCat(d.alpha(), ", ", d.beta()); | ||||
|   } | ||||
|   static std::string FormatResults(absl::Span<const result_t> results) { | ||||
|     return absl::StrJoin(results, ", "); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| template <typename R> | ||||
| struct DistributionFormatTraits<absl::zipf_distribution<R>> { | ||||
|   using distribution_t = absl::zipf_distribution<R>; | ||||
|   using result_t = typename distribution_t::result_type; | ||||
| 
 | ||||
|   static constexpr const char* Name() { return "Zipf"; } | ||||
| 
 | ||||
|   static std::string FunctionName() { | ||||
|     return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); | ||||
|   } | ||||
|   static std::string FormatArgs(const distribution_t& d) { | ||||
|     return absl::StrCat(d.k(), ", ", d.v(), ", ", d.q()); | ||||
|   } | ||||
|   static std::string FormatResults(absl::Span<const result_t> results) { | ||||
|     return absl::StrJoin(results, ", "); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| template <typename R> | ||||
| struct DistributionFormatTraits<absl::gaussian_distribution<R>> { | ||||
|   using distribution_t = absl::gaussian_distribution<R>; | ||||
|   using result_t = typename distribution_t::result_type; | ||||
| 
 | ||||
|   static constexpr const char* Name() { return "Gaussian"; } | ||||
| 
 | ||||
|   static std::string FunctionName() { | ||||
|     return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); | ||||
|   } | ||||
|   static std::string FormatArgs(const distribution_t& d) { | ||||
|     return absl::StrJoin(std::make_tuple(d.mean(), d.stddev()), ", "); | ||||
|   } | ||||
|   static std::string FormatResults(absl::Span<const result_t> results) { | ||||
|     return absl::StrJoin(results, ", "); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| template <typename R> | ||||
| struct DistributionFormatTraits<absl::log_uniform_int_distribution<R>> { | ||||
|   using distribution_t = absl::log_uniform_int_distribution<R>; | ||||
|   using result_t = typename distribution_t::result_type; | ||||
| 
 | ||||
|   static constexpr const char* Name() { return "LogUniform"; } | ||||
| 
 | ||||
|   static std::string FunctionName() { | ||||
|     return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); | ||||
|   } | ||||
|   static std::string FormatArgs(const distribution_t& d) { | ||||
|     return absl::StrJoin(std::make_tuple((d.min)(), (d.max)(), d.base()), ", "); | ||||
|   } | ||||
|   static std::string FormatResults(absl::Span<const result_t> results) { | ||||
|     return absl::StrJoin(results, ", "); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| template <typename NumType> | ||||
| struct UniformDistributionWrapper; | ||||
| 
 | ||||
| template <typename NumType> | ||||
| struct DistributionFormatTraits<UniformDistributionWrapper<NumType>> { | ||||
|   using distribution_t = UniformDistributionWrapper<NumType>; | ||||
|   using result_t = NumType; | ||||
| 
 | ||||
|   static constexpr const char* Name() { return "Uniform"; } | ||||
| 
 | ||||
|   static std::string FunctionName() { | ||||
|     return absl::StrCat(Name(), "<", ScalarTypeName<NumType>(), ">"); | ||||
|   } | ||||
|   static std::string FormatArgs(const distribution_t& d) { | ||||
|     return absl::StrCat((d.min)(), ", ", (d.max)()); | ||||
|   } | ||||
|   static std::string FormatResults(absl::Span<const result_t> results) { | ||||
|     return absl::StrJoin(results, ", "); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| }  // namespace random_internal
 | ||||
| ABSL_NAMESPACE_END | ||||
| }  // namespace absl
 | ||||
| 
 | ||||
| #endif  // ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_
 | ||||
|  | @ -55,7 +55,6 @@ | |||
| #include "absl/base/internal/inline_variable.h" | ||||
| #include "absl/random/bernoulli_distribution.h" | ||||
| #include "absl/random/beta_distribution.h" | ||||
| #include "absl/random/distribution_format_traits.h" | ||||
| #include "absl/random/exponential_distribution.h" | ||||
| #include "absl/random/gaussian_distribution.h" | ||||
| #include "absl/random/internal/distributions.h"  // IWYU pragma: export | ||||
|  | @ -126,14 +125,13 @@ Uniform(TagType tag, | |||
|         R lo, R hi) { | ||||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using distribution_t = random_internal::UniformDistributionWrapper<R>; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   auto a = random_internal::uniform_lower_bound(tag, lo, hi); | ||||
|   auto b = random_internal::uniform_upper_bound(tag, lo, hi); | ||||
|   if (a > b) return a; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg, tag, lo, hi); | ||||
|       distribution_t>(&urbg, tag, lo, hi); | ||||
| } | ||||
| 
 | ||||
| // absl::Uniform<T>(bitgen, lo, hi)
 | ||||
|  | @ -146,7 +144,6 @@ Uniform(URBG&& urbg,  // NOLINT(runtime/references) | |||
|         R lo, R hi) { | ||||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using distribution_t = random_internal::UniformDistributionWrapper<R>; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   constexpr auto tag = absl::IntervalClosedOpen; | ||||
|   auto a = random_internal::uniform_lower_bound(tag, lo, hi); | ||||
|  | @ -154,7 +151,7 @@ Uniform(URBG&& urbg,  // NOLINT(runtime/references) | |||
|   if (a > b) return a; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg, lo, hi); | ||||
|       distribution_t>(&urbg, lo, hi); | ||||
| } | ||||
| 
 | ||||
| // absl::Uniform(tag, bitgen, lo, hi)
 | ||||
|  | @ -172,14 +169,13 @@ Uniform(TagType tag, | |||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using return_t = typename random_internal::uniform_inferred_return_t<A, B>; | ||||
|   using distribution_t = random_internal::UniformDistributionWrapper<return_t>; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); | ||||
|   auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi); | ||||
|   if (a > b) return a; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg, tag, static_cast<return_t>(lo), | ||||
|       distribution_t>(&urbg, tag, static_cast<return_t>(lo), | ||||
|                                 static_cast<return_t>(hi)); | ||||
| } | ||||
| 
 | ||||
|  | @ -196,7 +192,6 @@ Uniform(URBG&& urbg,  // NOLINT(runtime/references) | |||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using return_t = typename random_internal::uniform_inferred_return_t<A, B>; | ||||
|   using distribution_t = random_internal::UniformDistributionWrapper<return_t>; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   constexpr auto tag = absl::IntervalClosedOpen; | ||||
|   auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); | ||||
|  | @ -204,7 +199,7 @@ Uniform(URBG&& urbg,  // NOLINT(runtime/references) | |||
|   if (a > b) return a; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg, static_cast<return_t>(lo), | ||||
|       distribution_t>(&urbg, static_cast<return_t>(lo), | ||||
|                                 static_cast<return_t>(hi)); | ||||
| } | ||||
| 
 | ||||
|  | @ -217,10 +212,9 @@ typename absl::enable_if_t<!std::is_signed<R>::value, R>  // | |||
| Uniform(URBG&& urbg) {  // NOLINT(runtime/references)
 | ||||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using distribution_t = random_internal::UniformDistributionWrapper<R>; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg); | ||||
|       distribution_t>(&urbg); | ||||
| } | ||||
| 
 | ||||
| // -----------------------------------------------------------------------------
 | ||||
|  | @ -248,10 +242,9 @@ bool Bernoulli(URBG&& urbg,  // NOLINT(runtime/references) | |||
|                double p) { | ||||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using distribution_t = absl::bernoulli_distribution; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg, p); | ||||
|       distribution_t>(&urbg, p); | ||||
| } | ||||
| 
 | ||||
| // -----------------------------------------------------------------------------
 | ||||
|  | @ -281,10 +274,9 @@ RealType Beta(URBG&& urbg,  // NOLINT(runtime/references) | |||
| 
 | ||||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using distribution_t = typename absl::beta_distribution<RealType>; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg, alpha, beta); | ||||
|       distribution_t>(&urbg, alpha, beta); | ||||
| } | ||||
| 
 | ||||
| // -----------------------------------------------------------------------------
 | ||||
|  | @ -314,10 +306,9 @@ RealType Exponential(URBG&& urbg,  // NOLINT(runtime/references) | |||
| 
 | ||||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using distribution_t = typename absl::exponential_distribution<RealType>; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg, lambda); | ||||
|       distribution_t>(&urbg, lambda); | ||||
| } | ||||
| 
 | ||||
| // -----------------------------------------------------------------------------
 | ||||
|  | @ -346,10 +337,9 @@ RealType Gaussian(URBG&& urbg,  // NOLINT(runtime/references) | |||
| 
 | ||||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using distribution_t = typename absl::gaussian_distribution<RealType>; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg, mean, stddev); | ||||
|       distribution_t>(&urbg, mean, stddev); | ||||
| } | ||||
| 
 | ||||
| // -----------------------------------------------------------------------------
 | ||||
|  | @ -389,10 +379,9 @@ IntType LogUniform(URBG&& urbg,  // NOLINT(runtime/references) | |||
| 
 | ||||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using distribution_t = typename absl::log_uniform_int_distribution<IntType>; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg, lo, hi, base); | ||||
|       distribution_t>(&urbg, lo, hi, base); | ||||
| } | ||||
| 
 | ||||
| // -----------------------------------------------------------------------------
 | ||||
|  | @ -420,10 +409,9 @@ IntType Poisson(URBG&& urbg,  // NOLINT(runtime/references) | |||
| 
 | ||||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using distribution_t = typename absl::poisson_distribution<IntType>; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg, mean); | ||||
|       distribution_t>(&urbg, mean); | ||||
| } | ||||
| 
 | ||||
| // -----------------------------------------------------------------------------
 | ||||
|  | @ -453,10 +441,9 @@ IntType Zipf(URBG&& urbg,  // NOLINT(runtime/references) | |||
| 
 | ||||
|   using gen_t = absl::decay_t<URBG>; | ||||
|   using distribution_t = typename absl::zipf_distribution<IntType>; | ||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; | ||||
| 
 | ||||
|   return random_internal::DistributionCaller<gen_t>::template Call< | ||||
|       distribution_t, format_t>(&urbg, hi, q, v); | ||||
|       distribution_t>(&urbg, hi, q, v); | ||||
| } | ||||
| 
 | ||||
| ABSL_NAMESPACE_END | ||||
|  |  | |||
|  | @ -33,20 +33,7 @@ struct DistributionCaller { | |||
|   // Call the provided distribution type. The parameters are expected
 | ||||
|   // to be explicitly specified.
 | ||||
|   // DistrT is the distribution type.
 | ||||
|   // FormatT is the formatter type:
 | ||||
|   //
 | ||||
|   // struct FormatT {
 | ||||
|   //   using result_type = distribution_t::result_type;
 | ||||
|   //   static std::string FormatCall(
 | ||||
|   //       const distribution_t& distr,
 | ||||
|   //       absl::Span<const result_type>);
 | ||||
|   //
 | ||||
|   //   static std::string FormatExpectation(
 | ||||
|   //       absl::string_view match_args,
 | ||||
|   //       absl::Span<const result_t> results);
 | ||||
|   // }
 | ||||
|   //
 | ||||
|   template <typename DistrT, typename FormatT, typename... Args> | ||||
|   template <typename DistrT, typename... Args> | ||||
|   static typename DistrT::result_type Call(URBG* urbg, Args&&... args) { | ||||
|     DistrT dist(std::forward<Args>(args)...); | ||||
|     return dist(*urbg); | ||||
|  |  | |||
|  | @ -16,8 +16,6 @@ | |||
| #ifndef ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ | ||||
| #define ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ | ||||
| 
 | ||||
| #include <atomic> | ||||
| #include <deque> | ||||
| #include <string> | ||||
| #include <typeinfo> | ||||
| 
 | ||||
|  | @ -28,27 +26,6 @@ namespace absl { | |||
| ABSL_NAMESPACE_BEGIN | ||||
| namespace random_internal { | ||||
| 
 | ||||
| // MockingBitGenExpectationFormatter is invoked to format unsatisfied mocks
 | ||||
| // and remaining results into a description string.
 | ||||
| template <typename DistrT, typename FormatT> | ||||
| struct MockingBitGenExpectationFormatter { | ||||
|   std::string operator()(absl::string_view args) { | ||||
|     return absl::StrCat(FormatT::FunctionName(), "(", args, ")"); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| // MockingBitGenCallFormatter is invoked to format each distribution call
 | ||||
| // into a description string for the mock log.
 | ||||
| template <typename DistrT, typename FormatT> | ||||
| struct MockingBitGenCallFormatter { | ||||
|   std::string operator()(const DistrT& dist, | ||||
|                          const typename DistrT::result_type& result) { | ||||
|     return absl::StrCat( | ||||
|         FormatT::FunctionName(), "(", FormatT::FormatArgs(dist), ") => {", | ||||
|         FormatT::FormatResults(absl::MakeSpan(&result, 1)), "}"); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| class MockingBitGenBase { | ||||
|   template <typename> | ||||
|   friend struct DistributionCaller; | ||||
|  | @ -61,14 +38,9 @@ class MockingBitGenBase { | |||
|   static constexpr result_type(max)() { return (generator_type::max)(); } | ||||
|   result_type operator()() { return gen_(); } | ||||
| 
 | ||||
|   MockingBitGenBase() : gen_(), observed_call_log_() {} | ||||
|   virtual ~MockingBitGenBase() = default; | ||||
| 
 | ||||
|  protected: | ||||
|   const std::deque<std::string>& observed_call_log() { | ||||
|     return observed_call_log_; | ||||
|   } | ||||
| 
 | ||||
|   // CallImpl is the type-erased virtual dispatch.
 | ||||
|   // The type of dist is always distribution<T>,
 | ||||
|   // The type of result is always distribution<T>::result_type.
 | ||||
|  | @ -81,10 +53,9 @@ class MockingBitGenBase { | |||
|   } | ||||
| 
 | ||||
|   // Call the generating distribution function.
 | ||||
|   // Invoked by DistributionCaller<>::Call<DistT, FormatT>.
 | ||||
|   // Invoked by DistributionCaller<>::Call<DistT>.
 | ||||
|   // DistT is the distribution type.
 | ||||
|   // FormatT is the distribution formatter traits type.
 | ||||
|   template <typename DistrT, typename FormatT, typename... Args> | ||||
|   template <typename DistrT, typename... Args> | ||||
|   typename DistrT::result_type Call(Args&&... args) { | ||||
|     using distr_result_type = typename DistrT::result_type; | ||||
|     using ArgTupleT = std::tuple<absl::decay_t<Args>...>; | ||||
|  | @ -100,17 +71,11 @@ class MockingBitGenBase { | |||
|       result = dist(gen_); | ||||
|     } | ||||
| 
 | ||||
|     // TODO(asoffer): Forwarding the args through means we no longer need to
 | ||||
|     // extract them from the from the distribution in formatter traits. We can
 | ||||
|     // just StrJoin them.
 | ||||
|     observed_call_log_.push_back( | ||||
|         MockingBitGenCallFormatter<DistrT, FormatT>{}(dist, result)); | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|  private: | ||||
|   generator_type gen_; | ||||
|   std::deque<std::string> observed_call_log_; | ||||
| };  // namespace random_internal
 | ||||
| 
 | ||||
| }  // namespace random_internal
 | ||||
|  |  | |||
|  | @ -1,30 +0,0 @@ | |||
| //
 | ||||
| // Copyright 2018 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/random/mocking_bit_gen.h" | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| namespace absl { | ||||
| ABSL_NAMESPACE_BEGIN | ||||
| MockingBitGen::~MockingBitGen() { | ||||
| 
 | ||||
|   for (const auto& del : deleters_) { | ||||
|     del(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| ABSL_NAMESPACE_END | ||||
| }  // namespace absl
 | ||||
|  | @ -100,7 +100,9 @@ class MockingBitGen : public absl::random_internal::MockingBitGenBase { | |||
|  public: | ||||
|   MockingBitGen() {} | ||||
| 
 | ||||
|   ~MockingBitGen() override; | ||||
|   ~MockingBitGen() override { | ||||
|     for (const auto& del : deleters_) del(); | ||||
|   } | ||||
| 
 | ||||
|  private: | ||||
|   template <typename DistrT, typename... Args> | ||||
|  | @ -182,10 +184,10 @@ namespace random_internal { | |||
| 
 | ||||
| template <> | ||||
| struct DistributionCaller<absl::MockingBitGen> { | ||||
|   template <typename DistrT, typename FormatT, typename... Args> | ||||
|   template <typename DistrT, typename... Args> | ||||
|   static typename DistrT::result_type Call(absl::MockingBitGen* gen, | ||||
|                                            Args&&... args) { | ||||
|     return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...); | ||||
|     return gen->template Call<DistrT>(std::forward<Args>(args)...); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -485,6 +485,7 @@ cc_test( | |||
|     copts = ABSL_TEST_COPTS, | ||||
|     visibility = ["//visibility:private"], | ||||
|     deps = [ | ||||
|         ":internal", | ||||
|         ":pow10_helper", | ||||
|         ":strings", | ||||
|         "//absl/base:config", | ||||
|  |  | |||
|  | @ -284,6 +284,7 @@ absl_cc_test( | |||
|     absl::raw_logging_internal | ||||
|     absl::random_random | ||||
|     absl::random_distributions | ||||
|     absl::strings_internal | ||||
|     gmock_main | ||||
| ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -120,24 +120,25 @@ class ConvertedIntInfo { | |||
| // the '#' flag is specified to modify the precision for 'o' conversions.
 | ||||
| string_view BaseIndicator(const ConvertedIntInfo &info, | ||||
|                           const ConversionSpec conv) { | ||||
|   bool alt = conv.flags().alt; | ||||
|   int radix = FormatConversionCharRadix(conv.conv()); | ||||
|   if (conv.conv() == ConversionChar::p) alt = true;  // always show 0x for %p.
 | ||||
|   bool alt = conv.has_alt_flag(); | ||||
|   int radix = FormatConversionCharRadix(conv.conversion_char()); | ||||
|   if (conv.conversion_char() == ConversionChar::p) | ||||
|     alt = true;  // always show 0x for %p.
 | ||||
|   // From the POSIX description of '#' flag:
 | ||||
|   //   "For x or X conversion specifiers, a non-zero result shall have
 | ||||
|   //   0x (or 0X) prefixed to it."
 | ||||
|   if (alt && radix == 16 && !info.digits().empty()) { | ||||
|     if (FormatConversionCharIsUpper(conv.conv())) return "0X"; | ||||
|     if (FormatConversionCharIsUpper(conv.conversion_char())) return "0X"; | ||||
|     return "0x"; | ||||
|   } | ||||
|   return {}; | ||||
| } | ||||
| 
 | ||||
| string_view SignColumn(bool neg, const ConversionSpec conv) { | ||||
|   if (FormatConversionCharIsSigned(conv.conv())) { | ||||
|   if (FormatConversionCharIsSigned(conv.conversion_char())) { | ||||
|     if (neg) return "-"; | ||||
|     if (conv.flags().show_pos) return "+"; | ||||
|     if (conv.flags().sign_col) return " "; | ||||
|     if (conv.has_show_pos_flag()) return "+"; | ||||
|     if (conv.has_sign_col_flag()) return " "; | ||||
|   } | ||||
|   return {}; | ||||
| } | ||||
|  | @ -147,9 +148,9 @@ bool ConvertCharImpl(unsigned char v, const ConversionSpec conv, | |||
|   size_t fill = 0; | ||||
|   if (conv.width() >= 0) fill = conv.width(); | ||||
|   ReducePadding(1, &fill); | ||||
|   if (!conv.flags().left) sink->Append(fill, ' '); | ||||
|   if (!conv.has_left_flag()) sink->Append(fill, ' '); | ||||
|   sink->Append(1, v); | ||||
|   if (conv.flags().left) sink->Append(fill, ' '); | ||||
|   if (conv.has_left_flag()) sink->Append(fill, ' '); | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
|  | @ -174,7 +175,7 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info, | |||
|   if (!precision_specified) | ||||
|     precision = 1; | ||||
| 
 | ||||
|   if (conv.flags().alt && conv.conv() == ConversionChar::o) { | ||||
|   if (conv.has_alt_flag() && conv.conversion_char() == ConversionChar::o) { | ||||
|     // From POSIX description of the '#' (alt) flag:
 | ||||
|     //   "For o conversion, it increases the precision (if necessary) to
 | ||||
|     //   force the first digit of the result to be zero."
 | ||||
|  | @ -187,13 +188,13 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info, | |||
|   size_t num_zeroes = Excess(formatted.size(), precision); | ||||
|   ReducePadding(num_zeroes, &fill); | ||||
| 
 | ||||
|   size_t num_left_spaces = !conv.flags().left ? fill : 0; | ||||
|   size_t num_right_spaces = conv.flags().left ? fill : 0; | ||||
|   size_t num_left_spaces = !conv.has_left_flag() ? fill : 0; | ||||
|   size_t num_right_spaces = conv.has_left_flag() ? fill : 0; | ||||
| 
 | ||||
|   // From POSIX description of the '0' (zero) flag:
 | ||||
|   //   "For d, i, o, u, x, and X conversion specifiers, if a precision
 | ||||
|   //   is specified, the '0' flag is ignored."
 | ||||
|   if (!precision_specified && conv.flags().zero) { | ||||
|   if (!precision_specified && conv.has_zero_flag()) { | ||||
|     num_zeroes += num_left_spaces; | ||||
|     num_left_spaces = 0; | ||||
|   } | ||||
|  | @ -209,8 +210,8 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info, | |||
| 
 | ||||
| template <typename T> | ||||
| bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) { | ||||
|   ConvertedIntInfo info(v, conv.conv()); | ||||
|   if (conv.flags().basic && (conv.conv() != ConversionChar::p)) { | ||||
|   ConvertedIntInfo info(v, conv.conversion_char()); | ||||
|   if (conv.is_basic() && (conv.conversion_char() != ConversionChar::p)) { | ||||
|     if (info.is_neg()) sink->Append(1, '-'); | ||||
|     if (info.digits().empty()) { | ||||
|       sink->Append(1, '0'); | ||||
|  | @ -224,13 +225,14 @@ bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) { | |||
| 
 | ||||
| template <typename T> | ||||
| bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { | ||||
|   if (FormatConversionCharIsFloat(conv.conv())) { | ||||
|   if (FormatConversionCharIsFloat(conv.conversion_char())) { | ||||
|     return FormatConvertImpl(static_cast<double>(v), conv, sink).value; | ||||
|   } | ||||
|   if (conv.conv() == ConversionChar::c) | ||||
|   if (conv.conversion_char() == ConversionChar::c) | ||||
|     return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); | ||||
|   if (!FormatConversionCharIsIntegral(conv.conv())) return false; | ||||
|   if (!FormatConversionCharIsSigned(conv.conv()) && IsSigned<T>::value) { | ||||
|   if (!FormatConversionCharIsIntegral(conv.conversion_char())) return false; | ||||
|   if (!FormatConversionCharIsSigned(conv.conversion_char()) && | ||||
|       IsSigned<T>::value) { | ||||
|     using U = typename MakeUnsigned<T>::type; | ||||
|     return FormatConvertImpl(static_cast<U>(v), conv, sink).value; | ||||
|   } | ||||
|  | @ -239,19 +241,19 @@ bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { | |||
| 
 | ||||
| template <typename T> | ||||
| bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { | ||||
|   return FormatConversionCharIsFloat(conv.conv()) && | ||||
|   return FormatConversionCharIsFloat(conv.conversion_char()) && | ||||
|          ConvertFloatImpl(v, conv, sink); | ||||
| } | ||||
| 
 | ||||
| inline bool ConvertStringArg(string_view v, const ConversionSpec conv, | ||||
|                              FormatSinkImpl *sink) { | ||||
|   if (conv.conv() != ConversionChar::s) return false; | ||||
|   if (conv.flags().basic) { | ||||
|   if (conv.conversion_char() != ConversionChar::s) return false; | ||||
|   if (conv.is_basic()) { | ||||
|     sink->Append(v); | ||||
|     return true; | ||||
|   } | ||||
|   return sink->PutPaddedString(v, conv.width(), conv.precision(), | ||||
|                                conv.flags().left); | ||||
|                                conv.has_left_flag()); | ||||
| } | ||||
| 
 | ||||
| }  // namespace
 | ||||
|  | @ -272,7 +274,7 @@ ConvertResult<Conv::s> FormatConvertImpl(string_view v, | |||
| ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, | ||||
|                                                    const ConversionSpec conv, | ||||
|                                                    FormatSinkImpl *sink) { | ||||
|   if (conv.conv() == ConversionChar::p) | ||||
|   if (conv.conversion_char() == ConversionChar::p) | ||||
|     return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; | ||||
|   size_t len; | ||||
|   if (v == nullptr) { | ||||
|  | @ -289,7 +291,7 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, | |||
| // ==================== Raw pointers ====================
 | ||||
| ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv, | ||||
|                                          FormatSinkImpl *sink) { | ||||
|   if (conv.conv() != ConversionChar::p) return {false}; | ||||
|   if (conv.conversion_char() != ConversionChar::p) return {false}; | ||||
|   if (!v.value) { | ||||
|     sink->Append("(nil)"); | ||||
|     return {true}; | ||||
|  |  | |||
|  | @ -70,9 +70,11 @@ template <class AbslCord, | |||
| ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, | ||||
|                                          ConversionSpec conv, | ||||
|                                          FormatSinkImpl* sink) { | ||||
|   if (conv.conv() != ConversionChar::s) return {false}; | ||||
|   if (conv.conversion_char() != ConversionChar::s) { | ||||
|     return {false}; | ||||
|   } | ||||
| 
 | ||||
|   bool is_left = conv.flags().left; | ||||
|   bool is_left = conv.has_left_flag(); | ||||
|   size_t space_remaining = 0; | ||||
| 
 | ||||
|   int width = conv.width(); | ||||
|  | @ -106,8 +108,8 @@ ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, | |||
| } | ||||
| 
 | ||||
| using IntegralConvertResult = | ||||
|     ConvertResult<Conv::c | Conv::numeric | Conv::star>; | ||||
| using FloatingConvertResult = ConvertResult<Conv::floating>; | ||||
|     ConvertResult<Conv::c | Conv::kNumeric | Conv::kStar>; | ||||
| using FloatingConvertResult = ConvertResult<Conv::kFloating>; | ||||
| 
 | ||||
| // Floats.
 | ||||
| FloatingConvertResult FormatConvertImpl(float v, ConversionSpec conv, | ||||
|  | @ -185,7 +187,9 @@ struct FormatCountCaptureHelper { | |||
|                                               FormatSinkImpl* sink) { | ||||
|     const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v; | ||||
| 
 | ||||
|     if (conv.conv() != str_format_internal::ConversionChar::n) return {false}; | ||||
|     if (conv.conversion_char() != str_format_internal::ConversionChar::n) { | ||||
|       return {false}; | ||||
|     } | ||||
|     *v2.p_ = static_cast<int>(sink->size()); | ||||
|     return {true}; | ||||
|   } | ||||
|  | @ -377,7 +381,7 @@ class FormatArgImpl { | |||
|   template <typename T> | ||||
|   static bool Dispatch(Data arg, ConversionSpec spec, void* out) { | ||||
|     // A `none` conv indicates that we want the `int` conversion.
 | ||||
|     if (ABSL_PREDICT_FALSE(spec.conv() == ConversionChar::none)) { | ||||
|     if (ABSL_PREDICT_FALSE(spec.conversion_char() == ConversionChar::kNone)) { | ||||
|       return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(), | ||||
|                       std::is_enum<T>()); | ||||
|     } | ||||
|  |  | |||
|  | @ -147,7 +147,7 @@ class SummarizingConverter { | |||
|        << FormatConversionSpecImplFriend::FlagsToString(bound); | ||||
|     if (bound.width() >= 0) ss << bound.width(); | ||||
|     if (bound.precision() >= 0) ss << "." << bound.precision(); | ||||
|     ss << bound.conv() << "}"; | ||||
|     ss << bound.conversion_char() << "}"; | ||||
|     Append(ss.str()); | ||||
|     return true; | ||||
|   } | ||||
|  |  | |||
|  | @ -9,13 +9,17 @@ ABSL_NAMESPACE_BEGIN | |||
| namespace str_format_internal { | ||||
| namespace { | ||||
| 
 | ||||
| std::string ConvToString(Conv conv) { | ||||
| std::string ConvToString(FormatConversionCharSet conv) { | ||||
|   std::string out; | ||||
| #define CONV_SET_CASE(c) \ | ||||
|   if (Contains(conv, Conv::c)) out += #c; | ||||
|   if (Contains(conv, FormatConversionCharSet::c)) { \ | ||||
|     out += #c; \ | ||||
|   } | ||||
|   ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) | ||||
| #undef CONV_SET_CASE | ||||
|   if (Contains(conv, Conv::star)) out += "*"; | ||||
|   if (Contains(conv, FormatConversionCharSet::kStar)) { | ||||
|     out += "*"; | ||||
|   } | ||||
|   return out; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -33,7 +33,7 @@ bool FallbackToSnprintf(const Float v, const ConversionSpec &conv, | |||
|     if (std::is_same<long double, Float>()) { | ||||
|       *fp++ = 'L'; | ||||
|     } | ||||
|     *fp++ = FormatConversionCharToChar(conv.conv()); | ||||
|     *fp++ = FormatConversionCharToChar(conv.conversion_char()); | ||||
|     *fp = 0; | ||||
|     assert(fp < fmt + sizeof(fmt)); | ||||
|   } | ||||
|  | @ -100,17 +100,19 @@ bool ConvertNonNumericFloats(char sign_char, Float v, | |||
|   char text[4], *ptr = text; | ||||
|   if (sign_char) *ptr++ = sign_char; | ||||
|   if (std::isnan(v)) { | ||||
|     ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "NAN" : "nan", | ||||
|                       3, ptr); | ||||
|     ptr = std::copy_n( | ||||
|         FormatConversionCharIsUpper(conv.conversion_char()) ? "NAN" : "nan", 3, | ||||
|         ptr); | ||||
|   } else if (std::isinf(v)) { | ||||
|     ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "INF" : "inf", | ||||
|                       3, ptr); | ||||
|     ptr = std::copy_n( | ||||
|         FormatConversionCharIsUpper(conv.conversion_char()) ? "INF" : "inf", 3, | ||||
|         ptr); | ||||
|   } else { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1, | ||||
|                                conv.flags().left); | ||||
|                                conv.has_left_flag()); | ||||
| } | ||||
| 
 | ||||
| // Round up the last digit of the value.
 | ||||
|  | @ -358,9 +360,9 @@ void WriteBufferToSink(char sign_char, string_view str, | |||
|                                        static_cast<int>(sign_char != 0), | ||||
|                                    0) | ||||
|                         : 0; | ||||
|   if (conv.flags().left) { | ||||
|   if (conv.has_left_flag()) { | ||||
|     right_spaces = missing_chars; | ||||
|   } else if (conv.flags().zero) { | ||||
|   } else if (conv.has_zero_flag()) { | ||||
|     zeros = missing_chars; | ||||
|   } else { | ||||
|     left_spaces = missing_chars; | ||||
|  | @ -382,9 +384,9 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, | |||
|   if (std::signbit(abs_v)) { | ||||
|     sign_char = '-'; | ||||
|     abs_v = -abs_v; | ||||
|   } else if (conv.flags().show_pos) { | ||||
|   } else if (conv.has_show_pos_flag()) { | ||||
|     sign_char = '+'; | ||||
|   } else if (conv.flags().sign_col) { | ||||
|   } else if (conv.has_sign_col_flag()) { | ||||
|     sign_char = ' '; | ||||
|   } | ||||
| 
 | ||||
|  | @ -401,14 +403,14 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, | |||
| 
 | ||||
|   Buffer buffer; | ||||
| 
 | ||||
|   switch (conv.conv()) { | ||||
|   switch (conv.conversion_char()) { | ||||
|     case ConversionChar::f: | ||||
|     case ConversionChar::F: | ||||
|       if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer, | ||||
|                                              nullptr)) { | ||||
|         return FallbackToSnprintf(v, conv, sink); | ||||
|       } | ||||
|       if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back(); | ||||
|       if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); | ||||
|       break; | ||||
| 
 | ||||
|     case ConversionChar::e: | ||||
|  | @ -417,9 +419,10 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, | |||
|                                                  &exp)) { | ||||
|         return FallbackToSnprintf(v, conv, sink); | ||||
|       } | ||||
|       if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back(); | ||||
|       PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', | ||||
|                     &buffer); | ||||
|       if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); | ||||
|       PrintExponent( | ||||
|           exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', | ||||
|           &buffer); | ||||
|       break; | ||||
| 
 | ||||
|     case ConversionChar::g: | ||||
|  | @ -446,13 +449,15 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, | |||
|         } | ||||
|         exp = 0; | ||||
|       } | ||||
|       if (!conv.flags().alt) { | ||||
|       if (!conv.has_alt_flag()) { | ||||
|         while (buffer.back() == '0') buffer.pop_back(); | ||||
|         if (buffer.back() == '.') buffer.pop_back(); | ||||
|       } | ||||
|       if (exp) { | ||||
|         PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', | ||||
|                       &buffer); | ||||
|         PrintExponent( | ||||
|             exp, | ||||
|             FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', | ||||
|             &buffer); | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|  |  | |||
|  | @ -52,7 +52,7 @@ TEST(ConversionCharTest, Names) { | |||
|     X(f), X(F), X(e), X(E), X(g), X(G), X(a), X(A),  // float
 | ||||
|     X(n), X(p),                                      // misc
 | ||||
| #undef X | ||||
|     {ConversionChar::none, '\0'}, | ||||
|     {ConversionChar::kNone, '\0'}, | ||||
|   }; | ||||
|   // clang-format on
 | ||||
|   for (auto e : kExpect) { | ||||
|  |  | |||
|  | @ -40,6 +40,7 @@ | |||
| #include "absl/random/distributions.h" | ||||
| #include "absl/random/random.h" | ||||
| #include "absl/strings/internal/numbers_test_common.h" | ||||
| #include "absl/strings/internal/ostringstream.h" | ||||
| #include "absl/strings/internal/pow10_helper.h" | ||||
| #include "absl/strings/str_cat.h" | ||||
| 
 | ||||
|  |  | |||
|  | @ -540,19 +540,19 @@ TEST_F(ParsedFormatTest, UncheckedCorrect) { | |||
|   EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f)); | ||||
| 
 | ||||
|   std::string format = "%sFFF%dZZZ%f"; | ||||
|   auto f2 = | ||||
|       ExtendedParsedFormat<Conv::string, Conv::d, Conv::floating>::New(format); | ||||
|   auto f2 = ExtendedParsedFormat<Conv::kString, Conv::d, Conv::kFloating>::New( | ||||
|       format); | ||||
| 
 | ||||
|   ASSERT_TRUE(f2); | ||||
|   EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2)); | ||||
| 
 | ||||
|   f2 = ExtendedParsedFormat<Conv::string, Conv::d, Conv::floating>::New( | ||||
|   f2 = ExtendedParsedFormat<Conv::kString, Conv::d, Conv::kFloating>::New( | ||||
|       "%s %d %f"); | ||||
| 
 | ||||
|   ASSERT_TRUE(f2); | ||||
|   EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2)); | ||||
| 
 | ||||
|   auto star = ExtendedParsedFormat<Conv::star, Conv::d>::New("%*d"); | ||||
|   auto star = ExtendedParsedFormat<Conv::kStar, Conv::d>::New("%*d"); | ||||
|   ASSERT_TRUE(star); | ||||
|   EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star)); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue