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 | # Add googletest directly to our build. This defines the gtest and gtest_main | ||||||
| # targets. | # targets. | ||||||
| add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src | add_subdirectory(${absl_gtest_src_dir} ${absl_gtest_build_dir} EXCLUDE_FROM_ALL) | ||||||
|                  ${CMAKE_BINARY_DIR}/googletest-build |  | ||||||
|                  EXCLUDE_FROM_ALL) |  | ||||||
|  |  | ||||||
|  | @ -212,7 +212,8 @@ ABSL_NAMESPACE_END | ||||||
| // aborts the program in release mode (when NDEBUG is defined). The
 | // aborts the program in release mode (when NDEBUG is defined). The
 | ||||||
| // implementation should abort the program as quickly as possible and ideally it
 | // implementation should abort the program as quickly as possible and ideally it
 | ||||||
| // should not be possible to ignore the abort request.
 | // 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__)) |     (defined(__GNUC__) && !defined(__clang__)) | ||||||
| #define ABSL_INTERNAL_HARDENING_ABORT() \ | #define ABSL_INTERNAL_HARDENING_ABORT() \ | ||||||
|   do {                                  \ |   do {                                  \ | ||||||
|  | @ -225,11 +226,11 @@ ABSL_NAMESPACE_END | ||||||
| 
 | 
 | ||||||
| // ABSL_HARDENING_ASSERT()
 | // 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
 | // runtime assertions that should be enabled in hardened builds even when
 | ||||||
| // `NDEBUG` is defined.
 | // `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()`.
 | // `ABSL_ASSERT()`.
 | ||||||
| //
 | //
 | ||||||
| // See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on
 | // 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.
 |   // Accessors for the key/value the iterator is pointing at.
 | ||||||
|   reference operator*() const { return node->value(position); } |   reference operator*() const { | ||||||
|   pointer operator->() const { return &node->value(position); } |     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++() { |   btree_iterator &operator++() { | ||||||
|     increment(); |     increment(); | ||||||
|  | @ -1769,6 +1774,7 @@ void btree_iterator<N, R, P>::increment_slow() { | ||||||
|       position = node->position(); |       position = node->position(); | ||||||
|       node = node->parent(); |       node = node->parent(); | ||||||
|     } |     } | ||||||
|  |     // TODO(ezb): assert we aren't incrementing end() instead of handling.
 | ||||||
|     if (position == node->finish()) { |     if (position == node->finish()) { | ||||||
|       *this = save; |       *this = save; | ||||||
|     } |     } | ||||||
|  | @ -1792,6 +1798,7 @@ void btree_iterator<N, R, P>::decrement_slow() { | ||||||
|       position = node->position() - 1; |       position = node->position() - 1; | ||||||
|       node = node->parent(); |       node = node->parent(); | ||||||
|     } |     } | ||||||
|  |     // TODO(ezb): assert we aren't decrementing begin() instead of handling.
 | ||||||
|     if (position < node->start()) { |     if (position < node->start()) { | ||||||
|       *this = save; |       *this = save; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -138,6 +138,7 @@ class node_handle<Policy, PolicyTraits, Alloc, | ||||||
|                   absl::void_t<typename Policy::mapped_type>> |                   absl::void_t<typename Policy::mapped_type>> | ||||||
|     : public node_handle_base<PolicyTraits, Alloc> { |     : public node_handle_base<PolicyTraits, Alloc> { | ||||||
|   using Base = node_handle_base<PolicyTraits, Alloc>; |   using Base = node_handle_base<PolicyTraits, Alloc>; | ||||||
|  |   using slot_type = typename PolicyTraits::slot_type; | ||||||
| 
 | 
 | ||||||
|  public: |  public: | ||||||
|   using key_type = typename Policy::key_type; |   using key_type = typename Policy::key_type; | ||||||
|  | @ -145,7 +146,7 @@ class node_handle<Policy, PolicyTraits, Alloc, | ||||||
| 
 | 
 | ||||||
|   constexpr node_handle() {} |   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()); |     return PolicyTraits::key(this->slot()); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -37,6 +37,9 @@ namespace absl { | ||||||
| ABSL_NAMESPACE_BEGIN | ABSL_NAMESPACE_BEGIN | ||||||
| namespace container_internal { | namespace container_internal { | ||||||
| 
 | 
 | ||||||
|  | template <size_t Alignment> | ||||||
|  | struct alignas(Alignment) AlignedType {}; | ||||||
|  | 
 | ||||||
| // Allocates at least n bytes aligned to the specified alignment.
 | // Allocates at least n bytes aligned to the specified alignment.
 | ||||||
| // Alignment must be a power of 2. It must be positive.
 | // 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) { | void* Allocate(Alloc* alloc, size_t n) { | ||||||
|   static_assert(Alignment > 0, ""); |   static_assert(Alignment > 0, ""); | ||||||
|   assert(n && "n must be positive"); |   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 A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; | ||||||
|   using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; |   using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; | ||||||
|   A mem_alloc(*alloc); |   A mem_alloc(*alloc); | ||||||
|  | @ -64,7 +67,7 @@ template <size_t Alignment, class Alloc> | ||||||
| void Deallocate(Alloc* alloc, void* p, size_t n) { | void Deallocate(Alloc* alloc, void* p, size_t n) { | ||||||
|   static_assert(Alignment > 0, ""); |   static_assert(Alignment > 0, ""); | ||||||
|   assert(n && "n must be positive"); |   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 A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; | ||||||
|   using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; |   using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; | ||||||
|   A mem_alloc(*alloc); |   A mem_alloc(*alloc); | ||||||
|  |  | ||||||
|  | @ -16,6 +16,8 @@ | ||||||
| 
 | 
 | ||||||
| #include <cstdint> | #include <cstdint> | ||||||
| #include <tuple> | #include <tuple> | ||||||
|  | #include <typeindex> | ||||||
|  | #include <typeinfo> | ||||||
| #include <utility> | #include <utility> | ||||||
| 
 | 
 | ||||||
| #include "gmock/gmock.h" | #include "gmock/gmock.h" | ||||||
|  | @ -27,6 +29,9 @@ ABSL_NAMESPACE_BEGIN | ||||||
| namespace container_internal { | namespace container_internal { | ||||||
| namespace { | namespace { | ||||||
| 
 | 
 | ||||||
|  | using ::testing::Gt; | ||||||
|  | using ::testing::_; | ||||||
|  | using ::testing::ElementsAre; | ||||||
| using ::testing::Pair; | using ::testing::Pair; | ||||||
| 
 | 
 | ||||||
| TEST(Memory, AlignmentLargerThanBase) { | TEST(Memory, AlignmentLargerThanBase) { | ||||||
|  | @ -45,6 +50,39 @@ TEST(Memory, AlignmentSmallerThanBase) { | ||||||
|   Deallocate<2>(&alloc, mem, 3); |   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 { | class Fixture : public ::testing::Test { | ||||||
|   using Alloc = std::allocator<std::string>; |   using Alloc = std::allocator<std::string>; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -104,6 +104,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "absl/base/internal/bits.h" | #include "absl/base/internal/bits.h" | ||||||
| #include "absl/base/internal/endian.h" | #include "absl/base/internal/endian.h" | ||||||
|  | #include "absl/base/macros.h" | ||||||
| #include "absl/base/port.h" | #include "absl/base/port.h" | ||||||
| #include "absl/container/internal/common.h" | #include "absl/container/internal/common.h" | ||||||
| #include "absl/container/internal/compressed_tuple.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) : ctrl_(ctrl) {}  // for end()
 | ||||||
|     iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {} |     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 { |     void assert_is_valid() const { | ||||||
|       assert(!ctrl_ || IsFull(*ctrl_) || *ctrl_ == kSentinel); |       ABSL_HARDENING_ASSERT(!ctrl_ || IsFull(*ctrl_) || *ctrl_ == kSentinel); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void skip_empty_or_deleted() { |     void skip_empty_or_deleted() { | ||||||
|  |  | ||||||
|  | @ -91,30 +91,30 @@ struct S2 { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| TEST_F(FlagTest, Traits) { | TEST_F(FlagTest, Traits) { | ||||||
|   EXPECT_EQ(flags::FlagValue::Kind<int>(), |   EXPECT_EQ(flags::StorageKind<int>(), | ||||||
|             flags::FlagValueStorageKind::kOneWordAtomic); |             flags::FlagValueStorageKind::kOneWordAtomic); | ||||||
|   EXPECT_EQ(flags::FlagValue::Kind<bool>(), |   EXPECT_EQ(flags::StorageKind<bool>(), | ||||||
|             flags::FlagValueStorageKind::kOneWordAtomic); |             flags::FlagValueStorageKind::kOneWordAtomic); | ||||||
|   EXPECT_EQ(flags::FlagValue::Kind<double>(), |   EXPECT_EQ(flags::StorageKind<double>(), | ||||||
|             flags::FlagValueStorageKind::kOneWordAtomic); |             flags::FlagValueStorageKind::kOneWordAtomic); | ||||||
|   EXPECT_EQ(flags::FlagValue::Kind<int64_t>(), |   EXPECT_EQ(flags::StorageKind<int64_t>(), | ||||||
|             flags::FlagValueStorageKind::kOneWordAtomic); |             flags::FlagValueStorageKind::kOneWordAtomic); | ||||||
| 
 | 
 | ||||||
| #if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) | #if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) | ||||||
|   EXPECT_EQ(flags::FlagValue::Kind<S1>(), |   EXPECT_EQ(flags::StorageKind<S1>(), | ||||||
|             flags::FlagValueStorageKind::kTwoWordsAtomic); |             flags::FlagValueStorageKind::kTwoWordsAtomic); | ||||||
|   EXPECT_EQ(flags::FlagValue::Kind<S2>(), |   EXPECT_EQ(flags::StorageKind<S2>(), | ||||||
|             flags::FlagValueStorageKind::kTwoWordsAtomic); |             flags::FlagValueStorageKind::kTwoWordsAtomic); | ||||||
| #else | #else | ||||||
|   EXPECT_EQ(flags::FlagValue::Kind<S1>(), |   EXPECT_EQ(flags::StorageKind<S1>(), | ||||||
|             flags::FlagValueStorageKind::kHeapAllocated); |             flags::FlagValueStorageKind::kHeapAllocated); | ||||||
|   EXPECT_EQ(flags::FlagValue::Kind<S2>(), |   EXPECT_EQ(flags::StorageKind<S2>(), | ||||||
|             flags::FlagValueStorageKind::kHeapAllocated); |             flags::FlagValueStorageKind::kHeapAllocated); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|   EXPECT_EQ(flags::FlagValue::Kind<std::string>(), |   EXPECT_EQ(flags::StorageKind<std::string>(), | ||||||
|             flags::FlagValueStorageKind::kHeapAllocated); |             flags::FlagValueStorageKind::kHeapAllocated); | ||||||
|   EXPECT_EQ(flags::FlagValue::Kind<std::vector<std::string>>(), |   EXPECT_EQ(flags::StorageKind<std::vector<std::string>>(), | ||||||
|             flags::FlagValueStorageKind::kHeapAllocated); |             flags::FlagValueStorageKind::kHeapAllocated); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -624,10 +624,10 @@ TEST_F(FlagTest, TestNonDefaultConstructibleType) { | ||||||
|   EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 25); |   EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 25); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // --------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| }  // namespace
 | }  // namespace
 | ||||||
| 
 | 
 | ||||||
|  | // --------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
| ABSL_RETIRED_FLAG(bool, old_bool_flag, true, "old descr"); | 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(int, old_int_flag, (int)std::sqrt(10), "old descr"); | ||||||
| ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr")); | ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr")); | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
| #include "absl/base/attributes.h" | #include "absl/base/attributes.h" | ||||||
|  | #include "absl/base/casts.h" | ||||||
| #include "absl/base/config.h" | #include "absl/base/config.h" | ||||||
| #include "absl/base/const_init.h" | #include "absl/base/const_init.h" | ||||||
| #include "absl/base/optimization.h" | #include "absl/base/optimization.h" | ||||||
|  | @ -135,18 +136,18 @@ void FlagImpl::Init() { | ||||||
|       (*default_value_.gen_func)(), DynValueDeleter{op_}); |       (*default_value_.gen_func)(), DynValueDeleter{op_}); | ||||||
|   switch (ValueStorageKind()) { |   switch (ValueStorageKind()) { | ||||||
|     case FlagValueStorageKind::kHeapAllocated: |     case FlagValueStorageKind::kHeapAllocated: | ||||||
|       value_.dynamic = init_value.release(); |       HeapAllocatedValue() = init_value.release(); | ||||||
|       break; |       break; | ||||||
|     case FlagValueStorageKind::kOneWordAtomic: { |     case FlagValueStorageKind::kOneWordAtomic: { | ||||||
|       int64_t atomic_value; |       int64_t atomic_value; | ||||||
|       std::memcpy(&atomic_value, init_value.get(), flags_internal::Sizeof(op_)); |       std::memcpy(&atomic_value, init_value.get(), Sizeof(op_)); | ||||||
|       value_.one_word_atomic.store(atomic_value, std::memory_order_release); |       OneWordValue().store(atomic_value, std::memory_order_release); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case FlagValueStorageKind::kTwoWordsAtomic: { |     case FlagValueStorageKind::kTwoWordsAtomic: { | ||||||
|       AlignedTwoWords atomic_value{0, 0}; |       AlignedTwoWords atomic_value{0, 0}; | ||||||
|       std::memcpy(&atomic_value, init_value.get(), flags_internal::Sizeof(op_)); |       std::memcpy(&atomic_value, init_value.get(), Sizeof(op_)); | ||||||
|       value_.two_words_atomic.store(atomic_value, std::memory_order_release); |       TwoWordsValue().store(atomic_value, std::memory_order_release); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | @ -198,18 +199,18 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const { | ||||||
| void FlagImpl::StoreValue(const void* src) { | void FlagImpl::StoreValue(const void* src) { | ||||||
|   switch (ValueStorageKind()) { |   switch (ValueStorageKind()) { | ||||||
|     case FlagValueStorageKind::kHeapAllocated: |     case FlagValueStorageKind::kHeapAllocated: | ||||||
|       flags_internal::Copy(op_, src, value_.dynamic); |       Copy(op_, src, HeapAllocatedValue()); | ||||||
|       break; |       break; | ||||||
|     case FlagValueStorageKind::kOneWordAtomic: { |     case FlagValueStorageKind::kOneWordAtomic: { | ||||||
|       int64_t one_word_val; |       int64_t one_word_val = 0; | ||||||
|       std::memcpy(&one_word_val, src, flags_internal::Sizeof(op_)); |       std::memcpy(&one_word_val, src, Sizeof(op_)); | ||||||
|       value_.one_word_atomic.store(one_word_val, std::memory_order_release); |       OneWordValue().store(one_word_val, std::memory_order_release); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case FlagValueStorageKind::kTwoWordsAtomic: { |     case FlagValueStorageKind::kTwoWordsAtomic: { | ||||||
|       AlignedTwoWords two_words_val{0, 0}; |       AlignedTwoWords two_words_val{0, 0}; | ||||||
|       std::memcpy(&two_words_val, src, flags_internal::Sizeof(op_)); |       std::memcpy(&two_words_val, src, Sizeof(op_)); | ||||||
|       value_.two_words_atomic.store(two_words_val, std::memory_order_release); |       TwoWordsValue().store(two_words_val, std::memory_order_release); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | @ -258,17 +259,19 @@ std::string FlagImpl::CurrentValue() const { | ||||||
|   switch (ValueStorageKind()) { |   switch (ValueStorageKind()) { | ||||||
|     case FlagValueStorageKind::kHeapAllocated: { |     case FlagValueStorageKind::kHeapAllocated: { | ||||||
|       absl::MutexLock l(guard); |       absl::MutexLock l(guard); | ||||||
|       return flags_internal::Unparse(op_, value_.dynamic); |       return flags_internal::Unparse(op_, HeapAllocatedValue()); | ||||||
|     } |     } | ||||||
|     case FlagValueStorageKind::kOneWordAtomic: { |     case FlagValueStorageKind::kOneWordAtomic: { | ||||||
|       const auto one_word_val = |       const auto one_word_val = | ||||||
|           value_.one_word_atomic.load(std::memory_order_acquire); |           absl::bit_cast<std::array<char, sizeof(int64_t)>>( | ||||||
|       return flags_internal::Unparse(op_, &one_word_val); |               OneWordValue().load(std::memory_order_acquire)); | ||||||
|  |       return flags_internal::Unparse(op_, one_word_val.data()); | ||||||
|     } |     } | ||||||
|     case FlagValueStorageKind::kTwoWordsAtomic: { |     case FlagValueStorageKind::kTwoWordsAtomic: { | ||||||
|       const auto two_words_val = |       const auto two_words_val = | ||||||
|           value_.two_words_atomic.load(std::memory_order_acquire); |           absl::bit_cast<std::array<char, sizeof(AlignedTwoWords)>>( | ||||||
|       return flags_internal::Unparse(op_, &two_words_val); |               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()) { |   switch (ValueStorageKind()) { | ||||||
|     case FlagValueStorageKind::kHeapAllocated: { |     case FlagValueStorageKind::kHeapAllocated: { | ||||||
|       return absl::make_unique<FlagState>( |       return absl::make_unique<FlagState>( | ||||||
|           this, flags_internal::Clone(op_, value_.dynamic), modified, |           this, flags_internal::Clone(op_, HeapAllocatedValue()), modified, | ||||||
|           on_command_line, counter_); |           on_command_line, counter_); | ||||||
|     } |     } | ||||||
|     case FlagValueStorageKind::kOneWordAtomic: { |     case FlagValueStorageKind::kOneWordAtomic: { | ||||||
|       return absl::make_unique<FlagState>( |       return absl::make_unique<FlagState>( | ||||||
|           this, value_.one_word_atomic.load(std::memory_order_acquire), |           this, OneWordValue().load(std::memory_order_acquire), modified, | ||||||
|           modified, on_command_line, counter_); |           on_command_line, counter_); | ||||||
|     } |     } | ||||||
|     case FlagValueStorageKind::kTwoWordsAtomic: { |     case FlagValueStorageKind::kTwoWordsAtomic: { | ||||||
|       return absl::make_unique<FlagState>( |       return absl::make_unique<FlagState>( | ||||||
|           this, value_.two_words_atomic.load(std::memory_order_acquire), |           this, TwoWordsValue().load(std::memory_order_acquire), modified, | ||||||
|           modified, on_command_line, counter_); |           on_command_line, counter_); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   return nullptr; |   return nullptr; | ||||||
|  | @ -359,6 +362,28 @@ bool FlagImpl::RestoreState(const FlagState& flag_state) { | ||||||
|   return true; |   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`
 | // Attempts to parse supplied `value` string using parsing routine in the `flag`
 | ||||||
| // argument. If parsing successful, this function replaces the dst with newly
 | // 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
 | // 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()) { |   switch (ValueStorageKind()) { | ||||||
|     case FlagValueStorageKind::kHeapAllocated: { |     case FlagValueStorageKind::kHeapAllocated: { | ||||||
|       absl::MutexLock l(guard); |       absl::MutexLock l(guard); | ||||||
| 
 |       flags_internal::CopyConstruct(op_, HeapAllocatedValue(), dst); | ||||||
|       flags_internal::CopyConstruct(op_, value_.dynamic, dst); |  | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case FlagValueStorageKind::kOneWordAtomic: { |     case FlagValueStorageKind::kOneWordAtomic: { | ||||||
|       const auto one_word_val = |       const int64_t one_word_val = | ||||||
|           value_.one_word_atomic.load(std::memory_order_acquire); |           OneWordValue().load(std::memory_order_acquire); | ||||||
|       std::memcpy(dst, &one_word_val, flags_internal::Sizeof(op_)); |       std::memcpy(dst, &one_word_val, Sizeof(op_)); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case FlagValueStorageKind::kTwoWordsAtomic: { |     case FlagValueStorageKind::kTwoWordsAtomic: { | ||||||
|       const auto two_words_val = |       const AlignedTwoWords two_words_val = | ||||||
|           value_.two_words_atomic.load(std::memory_order_acquire); |           TwoWordsValue().load(std::memory_order_acquire); | ||||||
|       std::memcpy(dst, &two_words_val, flags_internal::Sizeof(op_)); |       std::memcpy(dst, &two_words_val, Sizeof(op_)); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -53,56 +53,13 @@ enum class FlagOp { | ||||||
|   kStaticTypeId, |   kStaticTypeId, | ||||||
|   kParse, |   kParse, | ||||||
|   kUnparse, |   kUnparse, | ||||||
|  |   kValueOffset, | ||||||
| }; | }; | ||||||
| using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*); | using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*); | ||||||
| 
 | 
 | ||||||
| // Flag value specific operations routine.
 | // Forward declaration for Flag value specific operations.
 | ||||||
| template <typename T> | template <typename T> | ||||||
| void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { | 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; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // Deletes memory interpreting obj as flag value type pointer.
 | // Deletes memory interpreting obj as flag value type pointer.
 | ||||||
| inline void Delete(FlagOpFn op, const void* obj) { | inline void Delete(FlagOpFn op, const void* obj) { | ||||||
|  | @ -144,6 +101,16 @@ inline FlagStaticTypeId StaticTypeId(FlagOpFn op) { | ||||||
|   return reinterpret_cast<FlagStaticTypeId>( |   return reinterpret_cast<FlagStaticTypeId>( | ||||||
|       op(FlagOp::kStaticTypeId, nullptr, nullptr, nullptr)); |       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.
 | // Flag help auxiliary structs.
 | ||||||
|  | @ -239,6 +206,10 @@ using FlagUseOneWordStorage = std::integral_constant< | ||||||
| struct alignas(16) AlignedTwoWords { | struct alignas(16) AlignedTwoWords { | ||||||
|   int64_t first; |   int64_t first; | ||||||
|   int64_t second; |   int64_t second; | ||||||
|  | 
 | ||||||
|  |   bool IsInitialized() const { | ||||||
|  |     return first != flags_internal::UninitializedFlagValue(); | ||||||
|  |   } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
|  | @ -248,8 +219,14 @@ using FlagUseTwoWordsStorage = std::integral_constant< | ||||||
| #else | #else | ||||||
| // This is actually unused and only here to avoid ifdefs in other palces.
 | // This is actually unused and only here to avoid ifdefs in other palces.
 | ||||||
| struct AlignedTwoWords { | struct AlignedTwoWords { | ||||||
|   constexpr AlignedTwoWords() = default; |   constexpr AlignedTwoWords() noexcept : dummy() {} | ||||||
|   constexpr AlignedTwoWords(int64_t, int64_t) {} |   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
 | // This trait should be type dependent, otherwise SFINAE below will fail
 | ||||||
|  | @ -269,11 +246,8 @@ enum class FlagValueStorageKind : uint8_t { | ||||||
|   kTwoWordsAtomic = 2 |   kTwoWordsAtomic = 2 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| union FlagValue { |  | ||||||
|   constexpr explicit FlagValue(int64_t v) : one_word_atomic(v) {} |  | ||||||
| 
 |  | ||||||
| template <typename T> | template <typename T> | ||||||
|   static constexpr FlagValueStorageKind Kind() { | static constexpr FlagValueStorageKind StorageKind() { | ||||||
|   return FlagUseHeapStorage<T>::value |   return FlagUseHeapStorage<T>::value | ||||||
|              ? FlagValueStorageKind::kHeapAllocated |              ? FlagValueStorageKind::kHeapAllocated | ||||||
|              : FlagUseOneWordStorage<T>::value |              : FlagUseOneWordStorage<T>::value | ||||||
|  | @ -283,9 +257,59 @@ union FlagValue { | ||||||
|                          : FlagValueStorageKind::kHeapAllocated; |                          : FlagValueStorageKind::kHeapAllocated; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|   void* dynamic; | struct FlagHeapAllocatedValue { | ||||||
|   std::atomic<int64_t> one_word_atomic; |   using value_type = void*; | ||||||
|   std::atomic<flags_internal::AlignedTwoWords> two_words_atomic; | 
 | ||||||
|  |   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; | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 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), |         counter_(0), | ||||||
|         callback_(nullptr), |         callback_(nullptr), | ||||||
|         default_value_(default_value_gen), |         default_value_(default_value_gen), | ||||||
|         value_(flags_internal::UninitializedFlagValue()), |  | ||||||
|         data_guard_{} {} |         data_guard_{} {} | ||||||
| 
 | 
 | ||||||
|   // Constant access methods
 |   // Constant access methods
 | ||||||
|   void Read(void* dst) const override ABSL_LOCKS_EXCLUDED(*DataGuard()); |   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
 |   // Mutating access methods
 | ||||||
|   void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard()); |   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()); |       ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); | ||||||
|   // Flag initialization called via absl::call_once.
 |   // Flag initialization called via absl::call_once.
 | ||||||
|   void Init(); |   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,
 |   // Attempts to parse supplied `value` string. If parsing is successful,
 | ||||||
|   // returns new value. Otherwise returns nullptr.
 |   // returns new value. Otherwise returns nullptr.
 | ||||||
|   std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value, |   std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value, | ||||||
|  | @ -488,13 +506,6 @@ class FlagImpl final : public flags_internal::CommandLineFlag { | ||||||
|   // these two cases.
 |   // these two cases.
 | ||||||
|   FlagDefaultSrc default_value_; |   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
 |   // This is reserved space for an absl::Mutex to guard flag data. It will be
 | ||||||
|   // initialized in FlagImpl::Init via placement new.
 |   // initialized in FlagImpl::Init via placement new.
 | ||||||
|   // We can't use "absl::Mutex data_guard_", since this class is not literal.
 |   // We can't use "absl::Mutex data_guard_", since this class is not literal.
 | ||||||
|  | @ -514,8 +525,9 @@ class Flag { | ||||||
|  public: |  public: | ||||||
|   constexpr Flag(const char* name, const char* filename, const FlagHelpArg help, |   constexpr Flag(const char* name, const char* filename, const FlagHelpArg help, | ||||||
|                  const FlagDfltGenFunc default_value_gen) |                  const FlagDfltGenFunc default_value_gen) | ||||||
|       : impl_(name, filename, &FlagOps<T>, help, FlagValue::Kind<T>(), |       : impl_(name, filename, &FlagOps<T>, help, | ||||||
|               default_value_gen) {} |               flags_internal::StorageKind<T>(), default_value_gen), | ||||||
|  |         value_() {} | ||||||
| 
 | 
 | ||||||
|   T Get() const { |   T Get() const { | ||||||
|     // See implementation notes in CommandLineFlag::Get().
 |     // See implementation notes in CommandLineFlag::Get().
 | ||||||
|  | @ -530,7 +542,7 @@ class Flag { | ||||||
|     impl_.AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>); |     impl_.AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     impl_.Get(&u.value); |     if (!value_.Get(&u.value)) impl_.Read(&u.value); | ||||||
|     return std::move(u.value); |     return std::move(u.value); | ||||||
|   } |   } | ||||||
|   void Set(const T& v) { |   void Set(const T& v) { | ||||||
|  | @ -556,10 +568,63 @@ class Flag { | ||||||
|  private: |  private: | ||||||
|   template <typename U, bool do_register> |   template <typename U, bool do_register> | ||||||
|   friend class FlagRegistrar; |   friend class FlagRegistrar; | ||||||
|  | 
 | ||||||
|   // Flag's data
 |   // 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_; |   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
 | // This class facilitates Flag object registration and tail expression-based
 | ||||||
| // flag definition, for example:
 | // flag definition, for example:
 | ||||||
| // ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher);
 | // ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher);
 | ||||||
|  |  | ||||||
|  | @ -53,7 +53,6 @@ cc_library( | ||||||
|         "bernoulli_distribution.h", |         "bernoulli_distribution.h", | ||||||
|         "beta_distribution.h", |         "beta_distribution.h", | ||||||
|         "discrete_distribution.h", |         "discrete_distribution.h", | ||||||
|         "distribution_format_traits.h", |  | ||||||
|         "distributions.h", |         "distributions.h", | ||||||
|         "exponential_distribution.h", |         "exponential_distribution.h", | ||||||
|         "gaussian_distribution.h", |         "gaussian_distribution.h", | ||||||
|  | @ -141,16 +140,12 @@ cc_library( | ||||||
| cc_library( | cc_library( | ||||||
|     name = "mocking_bit_gen", |     name = "mocking_bit_gen", | ||||||
|     testonly = 1, |     testonly = 1, | ||||||
|     srcs = [ |  | ||||||
|         "mocking_bit_gen.cc", |  | ||||||
|     ], |  | ||||||
|     hdrs = [ |     hdrs = [ | ||||||
|         "mocking_bit_gen.h", |         "mocking_bit_gen.h", | ||||||
|     ], |     ], | ||||||
|     linkopts = ABSL_DEFAULT_LINKOPTS, |     linkopts = ABSL_DEFAULT_LINKOPTS, | ||||||
|     deps = [ |     deps = [ | ||||||
|         ":distributions", |         ":distributions", | ||||||
|         "//absl/base:raw_logging_internal", |  | ||||||
|         "//absl/container:flat_hash_map", |         "//absl/container:flat_hash_map", | ||||||
|         "//absl/meta:type_traits", |         "//absl/meta:type_traits", | ||||||
|         "//absl/random/internal:distribution_caller", |         "//absl/random/internal:distribution_caller", | ||||||
|  |  | ||||||
|  | @ -102,8 +102,6 @@ absl_cc_library( | ||||||
|   HDRS |   HDRS | ||||||
|     "mock_distributions.h" |     "mock_distributions.h" | ||||||
|     "mocking_bit_gen.h" |     "mocking_bit_gen.h" | ||||||
|   SRCS |  | ||||||
|     "mocking_bit_gen.cc" |  | ||||||
|   COPTS |   COPTS | ||||||
|     ${ABSL_DEFAULT_COPTS} |     ${ABSL_DEFAULT_COPTS} | ||||||
|   LINKOPTS |   LINKOPTS | ||||||
|  | @ -168,7 +166,6 @@ absl_cc_library( | ||||||
|     "bernoulli_distribution.h" |     "bernoulli_distribution.h" | ||||||
|     "beta_distribution.h" |     "beta_distribution.h" | ||||||
|     "discrete_distribution.h" |     "discrete_distribution.h" | ||||||
|     "distribution_format_traits.h" |  | ||||||
|     "distributions.h" |     "distributions.h" | ||||||
|     "exponential_distribution.h" |     "exponential_distribution.h" | ||||||
|     "gaussian_distribution.h" |     "gaussian_distribution.h" | ||||||
|  |  | ||||||
|  | @ -132,7 +132,7 @@ namespace random_internal { | ||||||
| 
 | 
 | ||||||
| template <> | template <> | ||||||
| struct DistributionCaller<absl::BitGenRef> { | 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, |   static typename DistrT::result_type Call(absl::BitGenRef* gen_ref, | ||||||
|                                            Args&&... args) { |                                            Args&&... args) { | ||||||
|     auto* mock_ptr = gen_ref->mocked_gen_ptr_; |     auto* mock_ptr = gen_ref->mocked_gen_ptr_; | ||||||
|  | @ -140,8 +140,7 @@ struct DistributionCaller<absl::BitGenRef> { | ||||||
|       DistrT dist(std::forward<Args>(args)...); |       DistrT dist(std::forward<Args>(args)...); | ||||||
|       return dist(*gen_ref); |       return dist(*gen_ref); | ||||||
|     } else { |     } else { | ||||||
|       return mock_ptr->template Call<DistrT, FormatT>( |       return mock_ptr->template Call<DistrT>(std::forward<Args>(args)...); | ||||||
|           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/base/internal/inline_variable.h" | ||||||
| #include "absl/random/bernoulli_distribution.h" | #include "absl/random/bernoulli_distribution.h" | ||||||
| #include "absl/random/beta_distribution.h" | #include "absl/random/beta_distribution.h" | ||||||
| #include "absl/random/distribution_format_traits.h" |  | ||||||
| #include "absl/random/exponential_distribution.h" | #include "absl/random/exponential_distribution.h" | ||||||
| #include "absl/random/gaussian_distribution.h" | #include "absl/random/gaussian_distribution.h" | ||||||
| #include "absl/random/internal/distributions.h"  // IWYU pragma: export | #include "absl/random/internal/distributions.h"  // IWYU pragma: export | ||||||
|  | @ -126,14 +125,13 @@ Uniform(TagType tag, | ||||||
|         R lo, R hi) { |         R lo, R hi) { | ||||||
|   using gen_t = absl::decay_t<URBG>; |   using gen_t = absl::decay_t<URBG>; | ||||||
|   using distribution_t = random_internal::UniformDistributionWrapper<R>; |   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 a = random_internal::uniform_lower_bound(tag, lo, hi); | ||||||
|   auto b = random_internal::uniform_upper_bound(tag, lo, hi); |   auto b = random_internal::uniform_upper_bound(tag, lo, hi); | ||||||
|   if (a > b) return a; |   if (a > b) return a; | ||||||
| 
 | 
 | ||||||
|   return random_internal::DistributionCaller<gen_t>::template Call< |   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)
 | // absl::Uniform<T>(bitgen, lo, hi)
 | ||||||
|  | @ -146,7 +144,6 @@ Uniform(URBG&& urbg,  // NOLINT(runtime/references) | ||||||
|         R lo, R hi) { |         R lo, R hi) { | ||||||
|   using gen_t = absl::decay_t<URBG>; |   using gen_t = absl::decay_t<URBG>; | ||||||
|   using distribution_t = random_internal::UniformDistributionWrapper<R>; |   using distribution_t = random_internal::UniformDistributionWrapper<R>; | ||||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; |  | ||||||
| 
 | 
 | ||||||
|   constexpr auto tag = absl::IntervalClosedOpen; |   constexpr auto tag = absl::IntervalClosedOpen; | ||||||
|   auto a = random_internal::uniform_lower_bound(tag, lo, hi); |   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; |   if (a > b) return a; | ||||||
| 
 | 
 | ||||||
|   return random_internal::DistributionCaller<gen_t>::template Call< |   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)
 | // absl::Uniform(tag, bitgen, lo, hi)
 | ||||||
|  | @ -172,14 +169,13 @@ Uniform(TagType tag, | ||||||
|   using gen_t = absl::decay_t<URBG>; |   using gen_t = absl::decay_t<URBG>; | ||||||
|   using return_t = typename random_internal::uniform_inferred_return_t<A, B>; |   using return_t = typename random_internal::uniform_inferred_return_t<A, B>; | ||||||
|   using distribution_t = random_internal::UniformDistributionWrapper<return_t>; |   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 a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); | ||||||
|   auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi); |   auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi); | ||||||
|   if (a > b) return a; |   if (a > b) return a; | ||||||
| 
 | 
 | ||||||
|   return random_internal::DistributionCaller<gen_t>::template Call< |   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)); |                                 static_cast<return_t>(hi)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -196,7 +192,6 @@ Uniform(URBG&& urbg,  // NOLINT(runtime/references) | ||||||
|   using gen_t = absl::decay_t<URBG>; |   using gen_t = absl::decay_t<URBG>; | ||||||
|   using return_t = typename random_internal::uniform_inferred_return_t<A, B>; |   using return_t = typename random_internal::uniform_inferred_return_t<A, B>; | ||||||
|   using distribution_t = random_internal::UniformDistributionWrapper<return_t>; |   using distribution_t = random_internal::UniformDistributionWrapper<return_t>; | ||||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; |  | ||||||
| 
 | 
 | ||||||
|   constexpr auto tag = absl::IntervalClosedOpen; |   constexpr auto tag = absl::IntervalClosedOpen; | ||||||
|   auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); |   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; |   if (a > b) return a; | ||||||
| 
 | 
 | ||||||
|   return random_internal::DistributionCaller<gen_t>::template Call< |   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)); |                                 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)
 | Uniform(URBG&& urbg) {  // NOLINT(runtime/references)
 | ||||||
|   using gen_t = absl::decay_t<URBG>; |   using gen_t = absl::decay_t<URBG>; | ||||||
|   using distribution_t = random_internal::UniformDistributionWrapper<R>; |   using distribution_t = random_internal::UniformDistributionWrapper<R>; | ||||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; |  | ||||||
| 
 | 
 | ||||||
|   return random_internal::DistributionCaller<gen_t>::template Call< |   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) { |                double p) { | ||||||
|   using gen_t = absl::decay_t<URBG>; |   using gen_t = absl::decay_t<URBG>; | ||||||
|   using distribution_t = absl::bernoulli_distribution; |   using distribution_t = absl::bernoulli_distribution; | ||||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; |  | ||||||
| 
 | 
 | ||||||
|   return random_internal::DistributionCaller<gen_t>::template Call< |   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 gen_t = absl::decay_t<URBG>; | ||||||
|   using distribution_t = typename absl::beta_distribution<RealType>; |   using distribution_t = typename absl::beta_distribution<RealType>; | ||||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; |  | ||||||
| 
 | 
 | ||||||
|   return random_internal::DistributionCaller<gen_t>::template Call< |   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 gen_t = absl::decay_t<URBG>; | ||||||
|   using distribution_t = typename absl::exponential_distribution<RealType>; |   using distribution_t = typename absl::exponential_distribution<RealType>; | ||||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; |  | ||||||
| 
 | 
 | ||||||
|   return random_internal::DistributionCaller<gen_t>::template Call< |   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 gen_t = absl::decay_t<URBG>; | ||||||
|   using distribution_t = typename absl::gaussian_distribution<RealType>; |   using distribution_t = typename absl::gaussian_distribution<RealType>; | ||||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; |  | ||||||
| 
 | 
 | ||||||
|   return random_internal::DistributionCaller<gen_t>::template Call< |   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 gen_t = absl::decay_t<URBG>; | ||||||
|   using distribution_t = typename absl::log_uniform_int_distribution<IntType>; |   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< |   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 gen_t = absl::decay_t<URBG>; | ||||||
|   using distribution_t = typename absl::poisson_distribution<IntType>; |   using distribution_t = typename absl::poisson_distribution<IntType>; | ||||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; |  | ||||||
| 
 | 
 | ||||||
|   return random_internal::DistributionCaller<gen_t>::template Call< |   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 gen_t = absl::decay_t<URBG>; | ||||||
|   using distribution_t = typename absl::zipf_distribution<IntType>; |   using distribution_t = typename absl::zipf_distribution<IntType>; | ||||||
|   using format_t = random_internal::DistributionFormatTraits<distribution_t>; |  | ||||||
| 
 | 
 | ||||||
|   return random_internal::DistributionCaller<gen_t>::template Call< |   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 | ABSL_NAMESPACE_END | ||||||
|  |  | ||||||
|  | @ -33,20 +33,7 @@ struct DistributionCaller { | ||||||
|   // Call the provided distribution type. The parameters are expected
 |   // Call the provided distribution type. The parameters are expected
 | ||||||
|   // to be explicitly specified.
 |   // to be explicitly specified.
 | ||||||
|   // DistrT is the distribution type.
 |   // DistrT is the distribution type.
 | ||||||
|   // FormatT is the formatter type:
 |   template <typename DistrT, typename... Args> | ||||||
|   //
 |  | ||||||
|   // 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> |  | ||||||
|   static typename DistrT::result_type Call(URBG* urbg, Args&&... args) { |   static typename DistrT::result_type Call(URBG* urbg, Args&&... args) { | ||||||
|     DistrT dist(std::forward<Args>(args)...); |     DistrT dist(std::forward<Args>(args)...); | ||||||
|     return dist(*urbg); |     return dist(*urbg); | ||||||
|  |  | ||||||
|  | @ -16,8 +16,6 @@ | ||||||
| #ifndef ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ | #ifndef ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ | ||||||
| #define ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ | #define ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ | ||||||
| 
 | 
 | ||||||
| #include <atomic> |  | ||||||
| #include <deque> |  | ||||||
| #include <string> | #include <string> | ||||||
| #include <typeinfo> | #include <typeinfo> | ||||||
| 
 | 
 | ||||||
|  | @ -28,27 +26,6 @@ namespace absl { | ||||||
| ABSL_NAMESPACE_BEGIN | ABSL_NAMESPACE_BEGIN | ||||||
| namespace random_internal { | 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 { | class MockingBitGenBase { | ||||||
|   template <typename> |   template <typename> | ||||||
|   friend struct DistributionCaller; |   friend struct DistributionCaller; | ||||||
|  | @ -61,14 +38,9 @@ class MockingBitGenBase { | ||||||
|   static constexpr result_type(max)() { return (generator_type::max)(); } |   static constexpr result_type(max)() { return (generator_type::max)(); } | ||||||
|   result_type operator()() { return gen_(); } |   result_type operator()() { return gen_(); } | ||||||
| 
 | 
 | ||||||
|   MockingBitGenBase() : gen_(), observed_call_log_() {} |  | ||||||
|   virtual ~MockingBitGenBase() = default; |   virtual ~MockingBitGenBase() = default; | ||||||
| 
 | 
 | ||||||
|  protected: |  protected: | ||||||
|   const std::deque<std::string>& observed_call_log() { |  | ||||||
|     return observed_call_log_; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // CallImpl is the type-erased virtual dispatch.
 |   // CallImpl is the type-erased virtual dispatch.
 | ||||||
|   // The type of dist is always distribution<T>,
 |   // The type of dist is always distribution<T>,
 | ||||||
|   // The type of result is always distribution<T>::result_type.
 |   // The type of result is always distribution<T>::result_type.
 | ||||||
|  | @ -81,10 +53,9 @@ class MockingBitGenBase { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // Call the generating distribution function.
 |   // Call the generating distribution function.
 | ||||||
|   // Invoked by DistributionCaller<>::Call<DistT, FormatT>.
 |   // Invoked by DistributionCaller<>::Call<DistT>.
 | ||||||
|   // DistT is the distribution type.
 |   // DistT is the distribution type.
 | ||||||
|   // FormatT is the distribution formatter traits type.
 |   template <typename DistrT, typename... Args> | ||||||
|   template <typename DistrT, typename FormatT, typename... Args> |  | ||||||
|   typename DistrT::result_type Call(Args&&... args) { |   typename DistrT::result_type Call(Args&&... args) { | ||||||
|     using distr_result_type = typename DistrT::result_type; |     using distr_result_type = typename DistrT::result_type; | ||||||
|     using ArgTupleT = std::tuple<absl::decay_t<Args>...>; |     using ArgTupleT = std::tuple<absl::decay_t<Args>...>; | ||||||
|  | @ -100,17 +71,11 @@ class MockingBitGenBase { | ||||||
|       result = dist(gen_); |       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; |     return result; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   generator_type gen_; |   generator_type gen_; | ||||||
|   std::deque<std::string> observed_call_log_; |  | ||||||
| };  // namespace random_internal
 | };  // namespace random_internal
 | ||||||
| 
 | 
 | ||||||
| }  // 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: |  public: | ||||||
|   MockingBitGen() {} |   MockingBitGen() {} | ||||||
| 
 | 
 | ||||||
|   ~MockingBitGen() override; |   ~MockingBitGen() override { | ||||||
|  |     for (const auto& del : deleters_) del(); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   template <typename DistrT, typename... Args> |   template <typename DistrT, typename... Args> | ||||||
|  | @ -182,10 +184,10 @@ namespace random_internal { | ||||||
| 
 | 
 | ||||||
| template <> | template <> | ||||||
| struct DistributionCaller<absl::MockingBitGen> { | 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, |   static typename DistrT::result_type Call(absl::MockingBitGen* gen, | ||||||
|                                            Args&&... args) { |                                            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, |     copts = ABSL_TEST_COPTS, | ||||||
|     visibility = ["//visibility:private"], |     visibility = ["//visibility:private"], | ||||||
|     deps = [ |     deps = [ | ||||||
|  |         ":internal", | ||||||
|         ":pow10_helper", |         ":pow10_helper", | ||||||
|         ":strings", |         ":strings", | ||||||
|         "//absl/base:config", |         "//absl/base:config", | ||||||
|  |  | ||||||
|  | @ -284,6 +284,7 @@ absl_cc_test( | ||||||
|     absl::raw_logging_internal |     absl::raw_logging_internal | ||||||
|     absl::random_random |     absl::random_random | ||||||
|     absl::random_distributions |     absl::random_distributions | ||||||
|  |     absl::strings_internal | ||||||
|     gmock_main |     gmock_main | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -120,24 +120,25 @@ class ConvertedIntInfo { | ||||||
| // the '#' flag is specified to modify the precision for 'o' conversions.
 | // the '#' flag is specified to modify the precision for 'o' conversions.
 | ||||||
| string_view BaseIndicator(const ConvertedIntInfo &info, | string_view BaseIndicator(const ConvertedIntInfo &info, | ||||||
|                           const ConversionSpec conv) { |                           const ConversionSpec conv) { | ||||||
|   bool alt = conv.flags().alt; |   bool alt = conv.has_alt_flag(); | ||||||
|   int radix = FormatConversionCharRadix(conv.conv()); |   int radix = FormatConversionCharRadix(conv.conversion_char()); | ||||||
|   if (conv.conv() == ConversionChar::p) alt = true;  // always show 0x for %p.
 |   if (conv.conversion_char() == ConversionChar::p) | ||||||
|  |     alt = true;  // always show 0x for %p.
 | ||||||
|   // From the POSIX description of '#' flag:
 |   // From the POSIX description of '#' flag:
 | ||||||
|   //   "For x or X conversion specifiers, a non-zero result shall have
 |   //   "For x or X conversion specifiers, a non-zero result shall have
 | ||||||
|   //   0x (or 0X) prefixed to it."
 |   //   0x (or 0X) prefixed to it."
 | ||||||
|   if (alt && radix == 16 && !info.digits().empty()) { |   if (alt && radix == 16 && !info.digits().empty()) { | ||||||
|     if (FormatConversionCharIsUpper(conv.conv())) return "0X"; |     if (FormatConversionCharIsUpper(conv.conversion_char())) return "0X"; | ||||||
|     return "0x"; |     return "0x"; | ||||||
|   } |   } | ||||||
|   return {}; |   return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| string_view SignColumn(bool neg, const ConversionSpec conv) { | string_view SignColumn(bool neg, const ConversionSpec conv) { | ||||||
|   if (FormatConversionCharIsSigned(conv.conv())) { |   if (FormatConversionCharIsSigned(conv.conversion_char())) { | ||||||
|     if (neg) return "-"; |     if (neg) return "-"; | ||||||
|     if (conv.flags().show_pos) return "+"; |     if (conv.has_show_pos_flag()) return "+"; | ||||||
|     if (conv.flags().sign_col) return " "; |     if (conv.has_sign_col_flag()) return " "; | ||||||
|   } |   } | ||||||
|   return {}; |   return {}; | ||||||
| } | } | ||||||
|  | @ -147,9 +148,9 @@ bool ConvertCharImpl(unsigned char v, const ConversionSpec conv, | ||||||
|   size_t fill = 0; |   size_t fill = 0; | ||||||
|   if (conv.width() >= 0) fill = conv.width(); |   if (conv.width() >= 0) fill = conv.width(); | ||||||
|   ReducePadding(1, &fill); |   ReducePadding(1, &fill); | ||||||
|   if (!conv.flags().left) sink->Append(fill, ' '); |   if (!conv.has_left_flag()) sink->Append(fill, ' '); | ||||||
|   sink->Append(1, v); |   sink->Append(1, v); | ||||||
|   if (conv.flags().left) sink->Append(fill, ' '); |   if (conv.has_left_flag()) sink->Append(fill, ' '); | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -174,7 +175,7 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info, | ||||||
|   if (!precision_specified) |   if (!precision_specified) | ||||||
|     precision = 1; |     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:
 |     // From POSIX description of the '#' (alt) flag:
 | ||||||
|     //   "For o conversion, it increases the precision (if necessary) to
 |     //   "For o conversion, it increases the precision (if necessary) to
 | ||||||
|     //   force the first digit of the result to be zero."
 |     //   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); |   size_t num_zeroes = Excess(formatted.size(), precision); | ||||||
|   ReducePadding(num_zeroes, &fill); |   ReducePadding(num_zeroes, &fill); | ||||||
| 
 | 
 | ||||||
|   size_t num_left_spaces = !conv.flags().left ? fill : 0; |   size_t num_left_spaces = !conv.has_left_flag() ? fill : 0; | ||||||
|   size_t num_right_spaces = conv.flags().left ? fill : 0; |   size_t num_right_spaces = conv.has_left_flag() ? fill : 0; | ||||||
| 
 | 
 | ||||||
|   // From POSIX description of the '0' (zero) flag:
 |   // From POSIX description of the '0' (zero) flag:
 | ||||||
|   //   "For d, i, o, u, x, and X conversion specifiers, if a precision
 |   //   "For d, i, o, u, x, and X conversion specifiers, if a precision
 | ||||||
|   //   is specified, the '0' flag is ignored."
 |   //   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_zeroes += num_left_spaces; | ||||||
|     num_left_spaces = 0; |     num_left_spaces = 0; | ||||||
|   } |   } | ||||||
|  | @ -209,8 +210,8 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info, | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) { | bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) { | ||||||
|   ConvertedIntInfo info(v, conv.conv()); |   ConvertedIntInfo info(v, conv.conversion_char()); | ||||||
|   if (conv.flags().basic && (conv.conv() != ConversionChar::p)) { |   if (conv.is_basic() && (conv.conversion_char() != ConversionChar::p)) { | ||||||
|     if (info.is_neg()) sink->Append(1, '-'); |     if (info.is_neg()) sink->Append(1, '-'); | ||||||
|     if (info.digits().empty()) { |     if (info.digits().empty()) { | ||||||
|       sink->Append(1, '0'); |       sink->Append(1, '0'); | ||||||
|  | @ -224,13 +225,14 @@ bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) { | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { | 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; |     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); |     return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); | ||||||
|   if (!FormatConversionCharIsIntegral(conv.conv())) return false; |   if (!FormatConversionCharIsIntegral(conv.conversion_char())) return false; | ||||||
|   if (!FormatConversionCharIsSigned(conv.conv()) && IsSigned<T>::value) { |   if (!FormatConversionCharIsSigned(conv.conversion_char()) && | ||||||
|  |       IsSigned<T>::value) { | ||||||
|     using U = typename MakeUnsigned<T>::type; |     using U = typename MakeUnsigned<T>::type; | ||||||
|     return FormatConvertImpl(static_cast<U>(v), conv, sink).value; |     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> | template <typename T> | ||||||
| bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { | bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { | ||||||
|   return FormatConversionCharIsFloat(conv.conv()) && |   return FormatConversionCharIsFloat(conv.conversion_char()) && | ||||||
|          ConvertFloatImpl(v, conv, sink); |          ConvertFloatImpl(v, conv, sink); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| inline bool ConvertStringArg(string_view v, const ConversionSpec conv, | inline bool ConvertStringArg(string_view v, const ConversionSpec conv, | ||||||
|                              FormatSinkImpl *sink) { |                              FormatSinkImpl *sink) { | ||||||
|   if (conv.conv() != ConversionChar::s) return false; |   if (conv.conversion_char() != ConversionChar::s) return false; | ||||||
|   if (conv.flags().basic) { |   if (conv.is_basic()) { | ||||||
|     sink->Append(v); |     sink->Append(v); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   return sink->PutPaddedString(v, conv.width(), conv.precision(), |   return sink->PutPaddedString(v, conv.width(), conv.precision(), | ||||||
|                                conv.flags().left); |                                conv.has_left_flag()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| }  // namespace
 | }  // namespace
 | ||||||
|  | @ -272,7 +274,7 @@ ConvertResult<Conv::s> FormatConvertImpl(string_view v, | ||||||
| ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, | ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, | ||||||
|                                                    const ConversionSpec conv, |                                                    const ConversionSpec conv, | ||||||
|                                                    FormatSinkImpl *sink) { |                                                    FormatSinkImpl *sink) { | ||||||
|   if (conv.conv() == ConversionChar::p) |   if (conv.conversion_char() == ConversionChar::p) | ||||||
|     return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; |     return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; | ||||||
|   size_t len; |   size_t len; | ||||||
|   if (v == nullptr) { |   if (v == nullptr) { | ||||||
|  | @ -289,7 +291,7 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, | ||||||
| // ==================== Raw pointers ====================
 | // ==================== Raw pointers ====================
 | ||||||
| ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv, | ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv, | ||||||
|                                          FormatSinkImpl *sink) { |                                          FormatSinkImpl *sink) { | ||||||
|   if (conv.conv() != ConversionChar::p) return {false}; |   if (conv.conversion_char() != ConversionChar::p) return {false}; | ||||||
|   if (!v.value) { |   if (!v.value) { | ||||||
|     sink->Append("(nil)"); |     sink->Append("(nil)"); | ||||||
|     return {true}; |     return {true}; | ||||||
|  |  | ||||||
|  | @ -70,9 +70,11 @@ template <class AbslCord, | ||||||
| ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, | ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, | ||||||
|                                          ConversionSpec conv, |                                          ConversionSpec conv, | ||||||
|                                          FormatSinkImpl* sink) { |                                          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; |   size_t space_remaining = 0; | ||||||
| 
 | 
 | ||||||
|   int width = conv.width(); |   int width = conv.width(); | ||||||
|  | @ -106,8 +108,8 @@ ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| using IntegralConvertResult = | using IntegralConvertResult = | ||||||
|     ConvertResult<Conv::c | Conv::numeric | Conv::star>; |     ConvertResult<Conv::c | Conv::kNumeric | Conv::kStar>; | ||||||
| using FloatingConvertResult = ConvertResult<Conv::floating>; | using FloatingConvertResult = ConvertResult<Conv::kFloating>; | ||||||
| 
 | 
 | ||||||
| // Floats.
 | // Floats.
 | ||||||
| FloatingConvertResult FormatConvertImpl(float v, ConversionSpec conv, | FloatingConvertResult FormatConvertImpl(float v, ConversionSpec conv, | ||||||
|  | @ -185,7 +187,9 @@ struct FormatCountCaptureHelper { | ||||||
|                                               FormatSinkImpl* sink) { |                                               FormatSinkImpl* sink) { | ||||||
|     const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v; |     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()); |     *v2.p_ = static_cast<int>(sink->size()); | ||||||
|     return {true}; |     return {true}; | ||||||
|   } |   } | ||||||
|  | @ -377,7 +381,7 @@ class FormatArgImpl { | ||||||
|   template <typename T> |   template <typename T> | ||||||
|   static bool Dispatch(Data arg, ConversionSpec spec, void* out) { |   static bool Dispatch(Data arg, ConversionSpec spec, void* out) { | ||||||
|     // A `none` conv indicates that we want the `int` conversion.
 |     // 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>(), |       return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(), | ||||||
|                       std::is_enum<T>()); |                       std::is_enum<T>()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -147,7 +147,7 @@ class SummarizingConverter { | ||||||
|        << FormatConversionSpecImplFriend::FlagsToString(bound); |        << FormatConversionSpecImplFriend::FlagsToString(bound); | ||||||
|     if (bound.width() >= 0) ss << bound.width(); |     if (bound.width() >= 0) ss << bound.width(); | ||||||
|     if (bound.precision() >= 0) ss << "." << bound.precision(); |     if (bound.precision() >= 0) ss << "." << bound.precision(); | ||||||
|     ss << bound.conv() << "}"; |     ss << bound.conversion_char() << "}"; | ||||||
|     Append(ss.str()); |     Append(ss.str()); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -9,13 +9,17 @@ ABSL_NAMESPACE_BEGIN | ||||||
| namespace str_format_internal { | namespace str_format_internal { | ||||||
| namespace { | namespace { | ||||||
| 
 | 
 | ||||||
| std::string ConvToString(Conv conv) { | std::string ConvToString(FormatConversionCharSet conv) { | ||||||
|   std::string out; |   std::string out; | ||||||
| #define CONV_SET_CASE(c) \ | #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, ) |   ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) | ||||||
| #undef CONV_SET_CASE | #undef CONV_SET_CASE | ||||||
|   if (Contains(conv, Conv::star)) out += "*"; |   if (Contains(conv, FormatConversionCharSet::kStar)) { | ||||||
|  |     out += "*"; | ||||||
|  |   } | ||||||
|   return out; |   return out; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -33,7 +33,7 @@ bool FallbackToSnprintf(const Float v, const ConversionSpec &conv, | ||||||
|     if (std::is_same<long double, Float>()) { |     if (std::is_same<long double, Float>()) { | ||||||
|       *fp++ = 'L'; |       *fp++ = 'L'; | ||||||
|     } |     } | ||||||
|     *fp++ = FormatConversionCharToChar(conv.conv()); |     *fp++ = FormatConversionCharToChar(conv.conversion_char()); | ||||||
|     *fp = 0; |     *fp = 0; | ||||||
|     assert(fp < fmt + sizeof(fmt)); |     assert(fp < fmt + sizeof(fmt)); | ||||||
|   } |   } | ||||||
|  | @ -100,17 +100,19 @@ bool ConvertNonNumericFloats(char sign_char, Float v, | ||||||
|   char text[4], *ptr = text; |   char text[4], *ptr = text; | ||||||
|   if (sign_char) *ptr++ = sign_char; |   if (sign_char) *ptr++ = sign_char; | ||||||
|   if (std::isnan(v)) { |   if (std::isnan(v)) { | ||||||
|     ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "NAN" : "nan", |     ptr = std::copy_n( | ||||||
|                       3, ptr); |         FormatConversionCharIsUpper(conv.conversion_char()) ? "NAN" : "nan", 3, | ||||||
|  |         ptr); | ||||||
|   } else if (std::isinf(v)) { |   } else if (std::isinf(v)) { | ||||||
|     ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "INF" : "inf", |     ptr = std::copy_n( | ||||||
|                       3, ptr); |         FormatConversionCharIsUpper(conv.conversion_char()) ? "INF" : "inf", 3, | ||||||
|  |         ptr); | ||||||
|   } else { |   } else { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1, |   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.
 | // 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), |                                        static_cast<int>(sign_char != 0), | ||||||
|                                    0) |                                    0) | ||||||
|                         : 0; |                         : 0; | ||||||
|   if (conv.flags().left) { |   if (conv.has_left_flag()) { | ||||||
|     right_spaces = missing_chars; |     right_spaces = missing_chars; | ||||||
|   } else if (conv.flags().zero) { |   } else if (conv.has_zero_flag()) { | ||||||
|     zeros = missing_chars; |     zeros = missing_chars; | ||||||
|   } else { |   } else { | ||||||
|     left_spaces = missing_chars; |     left_spaces = missing_chars; | ||||||
|  | @ -382,9 +384,9 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, | ||||||
|   if (std::signbit(abs_v)) { |   if (std::signbit(abs_v)) { | ||||||
|     sign_char = '-'; |     sign_char = '-'; | ||||||
|     abs_v = -abs_v; |     abs_v = -abs_v; | ||||||
|   } else if (conv.flags().show_pos) { |   } else if (conv.has_show_pos_flag()) { | ||||||
|     sign_char = '+'; |     sign_char = '+'; | ||||||
|   } else if (conv.flags().sign_col) { |   } else if (conv.has_sign_col_flag()) { | ||||||
|     sign_char = ' '; |     sign_char = ' '; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -401,14 +403,14 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, | ||||||
| 
 | 
 | ||||||
|   Buffer buffer; |   Buffer buffer; | ||||||
| 
 | 
 | ||||||
|   switch (conv.conv()) { |   switch (conv.conversion_char()) { | ||||||
|     case ConversionChar::f: |     case ConversionChar::f: | ||||||
|     case ConversionChar::F: |     case ConversionChar::F: | ||||||
|       if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer, |       if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer, | ||||||
|                                              nullptr)) { |                                              nullptr)) { | ||||||
|         return FallbackToSnprintf(v, conv, sink); |         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; |       break; | ||||||
| 
 | 
 | ||||||
|     case ConversionChar::e: |     case ConversionChar::e: | ||||||
|  | @ -417,8 +419,9 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, | ||||||
|                                                  &exp)) { |                                                  &exp)) { | ||||||
|         return FallbackToSnprintf(v, conv, sink); |         return FallbackToSnprintf(v, conv, sink); | ||||||
|       } |       } | ||||||
|       if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back(); |       if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); | ||||||
|       PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', |       PrintExponent( | ||||||
|  |           exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', | ||||||
|           &buffer); |           &buffer); | ||||||
|       break; |       break; | ||||||
| 
 | 
 | ||||||
|  | @ -446,12 +449,14 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, | ||||||
|         } |         } | ||||||
|         exp = 0; |         exp = 0; | ||||||
|       } |       } | ||||||
|       if (!conv.flags().alt) { |       if (!conv.has_alt_flag()) { | ||||||
|         while (buffer.back() == '0') buffer.pop_back(); |         while (buffer.back() == '0') buffer.pop_back(); | ||||||
|         if (buffer.back() == '.') buffer.pop_back(); |         if (buffer.back() == '.') buffer.pop_back(); | ||||||
|       } |       } | ||||||
|       if (exp) { |       if (exp) { | ||||||
|         PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', |         PrintExponent( | ||||||
|  |             exp, | ||||||
|  |             FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', | ||||||
|             &buffer); |             &buffer); | ||||||
|       } |       } | ||||||
|       break; |       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(f), X(F), X(e), X(E), X(g), X(G), X(a), X(A),  // float
 | ||||||
|     X(n), X(p),                                      // misc
 |     X(n), X(p),                                      // misc
 | ||||||
| #undef X | #undef X | ||||||
|     {ConversionChar::none, '\0'}, |     {ConversionChar::kNone, '\0'}, | ||||||
|   }; |   }; | ||||||
|   // clang-format on
 |   // clang-format on
 | ||||||
|   for (auto e : kExpect) { |   for (auto e : kExpect) { | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ | ||||||
| #include "absl/random/distributions.h" | #include "absl/random/distributions.h" | ||||||
| #include "absl/random/random.h" | #include "absl/random/random.h" | ||||||
| #include "absl/strings/internal/numbers_test_common.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/internal/pow10_helper.h" | ||||||
| #include "absl/strings/str_cat.h" | #include "absl/strings/str_cat.h" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -540,19 +540,19 @@ TEST_F(ParsedFormatTest, UncheckedCorrect) { | ||||||
|   EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f)); |   EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f)); | ||||||
| 
 | 
 | ||||||
|   std::string format = "%sFFF%dZZZ%f"; |   std::string format = "%sFFF%dZZZ%f"; | ||||||
|   auto f2 = |   auto f2 = ExtendedParsedFormat<Conv::kString, Conv::d, Conv::kFloating>::New( | ||||||
|       ExtendedParsedFormat<Conv::string, Conv::d, Conv::floating>::New(format); |       format); | ||||||
| 
 | 
 | ||||||
|   ASSERT_TRUE(f2); |   ASSERT_TRUE(f2); | ||||||
|   EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*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"); |       "%s %d %f"); | ||||||
| 
 | 
 | ||||||
|   ASSERT_TRUE(f2); |   ASSERT_TRUE(f2); | ||||||
|   EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*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); |   ASSERT_TRUE(star); | ||||||
|   EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star)); |   EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star)); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue