1 // 2 // Copyright 2018 The Abseil Authors. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // https://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 // ----------------------------------------------------------------------------- 17 // File: bit_gen_ref.h 18 // ----------------------------------------------------------------------------- 19 // 20 // This header defines a bit generator "reference" class, for use in interfaces 21 // that take both Abseil (e.g. `absl::BitGen`) and standard library (e.g. 22 // `std::mt19937`) bit generators. 23 24 #ifndef ABSL_RANDOM_BIT_GEN_REF_H_ 25 #define ABSL_RANDOM_BIT_GEN_REF_H_ 26 27 #include <limits> 28 #include <type_traits> 29 #include <utility> 30 31 #include "absl/base/attributes.h" 32 #include "absl/base/internal/fast_type_id.h" 33 #include "absl/base/macros.h" 34 #include "absl/meta/type_traits.h" 35 #include "absl/random/internal/distribution_caller.h" 36 #include "absl/random/internal/fast_uniform_bits.h" 37 38 namespace absl { 39 ABSL_NAMESPACE_BEGIN 40 namespace random_internal { 41 42 template <typename URBG, typename = void, typename = void, typename = void> 43 struct is_urbg : std::false_type {}; 44 45 template <typename URBG> 46 struct is_urbg< 47 URBG, 48 absl::enable_if_t<std::is_same< 49 typename URBG::result_type, 50 typename std::decay<decltype((URBG::min)())>::type>::value>, 51 absl::enable_if_t<std::is_same< 52 typename URBG::result_type, 53 typename std::decay<decltype((URBG::max)())>::type>::value>, 54 absl::enable_if_t<std::is_same< 55 typename URBG::result_type, 56 typename std::decay<decltype(std::declval<URBG>()())>::type>::value>> 57 : std::true_type {}; 58 59 template <typename> 60 struct DistributionCaller; 61 class MockHelpers; 62 63 } // namespace random_internal 64 65 // ----------------------------------------------------------------------------- 66 // absl::BitGenRef 67 // ----------------------------------------------------------------------------- 68 // 69 // `absl::BitGenRef` is a type-erasing class that provides a generator-agnostic 70 // non-owning "reference" interface for use in place of any specific uniform 71 // random bit generator (URBG). This class may be used for both Abseil 72 // (e.g. `absl::BitGen`, `absl::InsecureBitGen`) and Standard library (e.g 73 // `std::mt19937`, `std::minstd_rand`) bit generators. 74 // 75 // Like other reference classes, `absl::BitGenRef` does not own the 76 // underlying bit generator, and the underlying instance must outlive the 77 // `absl::BitGenRef`. 78 // 79 // `absl::BitGenRef` is particularly useful when used with an 80 // `absl::MockingBitGen` to test specific paths in functions which use random 81 // values. 82 // 83 // Example: 84 // void TakesBitGenRef(absl::BitGenRef gen) { 85 // int x = absl::Uniform<int>(gen, 0, 1000); 86 // } 87 // 88 class BitGenRef { 89 // SFINAE to detect whether the URBG type includes a member matching 90 // bool InvokeMock(base_internal::FastTypeIdType, void*, void*). 91 // 92 // These live inside BitGenRef so that they have friend access 93 // to MockingBitGen. (see similar methods in DistributionCaller). 94 template <template <class...> class Trait, class AlwaysVoid, class... Args> 95 struct detector : std::false_type {}; 96 template <template <class...> class Trait, class... Args> 97 struct detector<Trait, absl::void_t<Trait<Args...>>, Args...> 98 : std::true_type {}; 99 100 template <class T> 101 using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock( 102 std::declval<base_internal::FastTypeIdType>(), std::declval<void*>(), 103 std::declval<void*>())); 104 105 template <typename T> 106 using HasInvokeMock = typename detector<invoke_mock_t, void, T>::type; 107 108 public: 109 BitGenRef(const BitGenRef&) = default; 110 BitGenRef(BitGenRef&&) = default; 111 BitGenRef& operator=(const BitGenRef&) = default; 112 BitGenRef& operator=(BitGenRef&&) = default; 113 114 template < 115 typename URBGRef, typename URBG = absl::remove_cvref_t<URBGRef>, 116 typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value && 117 random_internal::is_urbg<URBG>::value && 118 !HasInvokeMock<URBG>::value)>* = nullptr> 119 BitGenRef(URBGRef&& gen ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT 120 : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), 121 mock_call_(NotAMock), 122 generate_impl_fn_(ImplFn<URBG>) {} 123 124 template <typename URBGRef, typename URBG = absl::remove_cvref_t<URBGRef>, 125 typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value && 126 random_internal::is_urbg<URBG>::value && 127 HasInvokeMock<URBG>::value)>* = nullptr> 128 BitGenRef(URBGRef&& gen ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT 129 : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), 130 mock_call_(&MockCall<URBG>), 131 generate_impl_fn_(ImplFn<URBG>) {} 132 133 using result_type = uint64_t; 134 135 static constexpr result_type(min)() { 136 return (std::numeric_limits<result_type>::min)(); 137 } 138 139 static constexpr result_type(max)() { 140 return (std::numeric_limits<result_type>::max)(); 141 } 142 143 result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); } 144 145 private: 146 using impl_fn = result_type (*)(uintptr_t); 147 using mock_call_fn = bool (*)(uintptr_t, base_internal::FastTypeIdType, void*, 148 void*); 149 150 template <typename URBG> 151 static result_type ImplFn(uintptr_t ptr) { 152 // Ensure that the return values from operator() fill the entire 153 // range promised by result_type, min() and max(). 154 absl::random_internal::FastUniformBits<result_type> fast_uniform_bits; 155 return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr)); 156 } 157 158 // Get a type-erased InvokeMock pointer. 159 template <typename URBG> 160 static bool MockCall(uintptr_t gen_ptr, base_internal::FastTypeIdType type, 161 void* result, void* arg_tuple) { 162 return reinterpret_cast<URBG*>(gen_ptr)->InvokeMock(type, result, 163 arg_tuple); 164 } 165 static bool NotAMock(uintptr_t, base_internal::FastTypeIdType, void*, void*) { 166 return false; 167 } 168 169 inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple, 170 void* result) { 171 if (mock_call_ == NotAMock) return false; // avoids an indirect call. 172 return mock_call_(t_erased_gen_ptr_, type, args_tuple, result); 173 } 174 175 uintptr_t t_erased_gen_ptr_; 176 mock_call_fn mock_call_; 177 impl_fn generate_impl_fn_; 178 179 template <typename> 180 friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock 181 friend class ::absl::random_internal::MockHelpers; // for InvokeMock 182 }; 183 184 ABSL_NAMESPACE_END 185 } // namespace absl 186 187 #endif // ABSL_RANDOM_BIT_GEN_REF_H_ 188