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 "absl/base/internal/fast_type_id.h" 28 #include "absl/base/macros.h" 29 #include "absl/meta/type_traits.h" 30 #include "absl/random/internal/distribution_caller.h" 31 #include "absl/random/internal/fast_uniform_bits.h" 32 33 namespace absl { 34 ABSL_NAMESPACE_BEGIN 35 namespace random_internal { 36 37 template <typename URBG, typename = void, typename = void, typename = void> 38 struct is_urbg : std::false_type {}; 39 40 template <typename URBG> 41 struct is_urbg< 42 URBG, 43 absl::enable_if_t<std::is_same< 44 typename URBG::result_type, 45 typename std::decay<decltype((URBG::min)())>::type>::value>, 46 absl::enable_if_t<std::is_same< 47 typename URBG::result_type, 48 typename std::decay<decltype((URBG::max)())>::type>::value>, 49 absl::enable_if_t<std::is_same< 50 typename URBG::result_type, 51 typename std::decay<decltype(std::declval<URBG>()())>::type>::value>> 52 : std::true_type {}; 53 54 template <typename> 55 struct DistributionCaller; 56 class MockHelpers; 57 58 } // namespace random_internal 59 60 // ----------------------------------------------------------------------------- 61 // absl::BitGenRef 62 // ----------------------------------------------------------------------------- 63 // 64 // `absl::BitGenRef` is a type-erasing class that provides a generator-agnostic 65 // non-owning "reference" interface for use in place of any specific uniform 66 // random bit generator (URBG). This class may be used for both Abseil 67 // (e.g. `absl::BitGen`, `absl::InsecureBitGen`) and Standard library (e.g 68 // `std::mt19937`, `std::minstd_rand`) bit generators. 69 // 70 // Like other reference classes, `absl::BitGenRef` does not own the 71 // underlying bit generator, and the underlying instance must outlive the 72 // `absl::BitGenRef`. 73 // 74 // `absl::BitGenRef` is particularly useful when used with an 75 // `absl::MockingBitGen` to test specific paths in functions which use random 76 // values. 77 // 78 // Example: 79 // void TakesBitGenRef(absl::BitGenRef gen) { 80 // int x = absl::Uniform<int>(gen, 0, 1000); 81 // } 82 // 83 class BitGenRef { 84 // SFINAE to detect whether the URBG type includes a member matching 85 // bool InvokeMock(base_internal::FastTypeIdType, void*, void*). 86 // 87 // These live inside BitGenRef so that they have friend access 88 // to MockingBitGen. (see similar methods in DistributionCaller). 89 template <template <class...> class Trait, class AlwaysVoid, class... Args> 90 struct detector : std::false_type {}; 91 template <template <class...> class Trait, class... Args> 92 struct detector<Trait, absl::void_t<Trait<Args...>>, Args...> 93 : std::true_type {}; 94 95 template <class T> 96 using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock( 97 std::declval<base_internal::FastTypeIdType>(), std::declval<void*>(), 98 std::declval<void*>())); 99 100 template <typename T> 101 using HasInvokeMock = typename detector<invoke_mock_t, void, T>::type; 102 103 public: 104 BitGenRef(const BitGenRef&) = default; 105 BitGenRef(BitGenRef&&) = default; 106 BitGenRef& operator=(const BitGenRef&) = default; 107 BitGenRef& operator=(BitGenRef&&) = default; 108 109 template <typename URBG, typename absl::enable_if_t< 110 (!std::is_same<URBG, BitGenRef>::value && 111 random_internal::is_urbg<URBG>::value && 112 !HasInvokeMock<URBG>::value)>* = nullptr> 113 BitGenRef(URBG& gen) // NOLINT 114 : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), 115 mock_call_(NotAMock), 116 generate_impl_fn_(ImplFn<URBG>) {} 117 118 template <typename URBG, 119 typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value && 120 random_internal::is_urbg<URBG>::value && 121 HasInvokeMock<URBG>::value)>* = nullptr> 122 BitGenRef(URBG& gen) // NOLINT 123 : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), 124 mock_call_(&MockCall<URBG>), 125 generate_impl_fn_(ImplFn<URBG>) {} 126 127 using result_type = uint64_t; 128 129 static constexpr result_type(min)() { 130 return (std::numeric_limits<result_type>::min)(); 131 } 132 133 static constexpr result_type(max)() { 134 return (std::numeric_limits<result_type>::max)(); 135 } 136 137 result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); } 138 139 private: 140 using impl_fn = result_type (*)(uintptr_t); 141 using mock_call_fn = bool (*)(uintptr_t, base_internal::FastTypeIdType, void*, 142 void*); 143 144 template <typename URBG> 145 static result_type ImplFn(uintptr_t ptr) { 146 // Ensure that the return values from operator() fill the entire 147 // range promised by result_type, min() and max(). 148 absl::random_internal::FastUniformBits<result_type> fast_uniform_bits; 149 return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr)); 150 } 151 152 // Get a type-erased InvokeMock pointer. 153 template <typename URBG> 154 static bool MockCall(uintptr_t gen_ptr, base_internal::FastTypeIdType type, 155 void* result, void* arg_tuple) { 156 return reinterpret_cast<URBG*>(gen_ptr)->InvokeMock(type, result, 157 arg_tuple); 158 } 159 static bool NotAMock(uintptr_t, base_internal::FastTypeIdType, void*, void*) { 160 return false; 161 } 162 163 inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple, 164 void* result) { 165 if (mock_call_ == NotAMock) return false; // avoids an indirect call. 166 return mock_call_(t_erased_gen_ptr_, type, args_tuple, result); 167 } 168 169 uintptr_t t_erased_gen_ptr_; 170 mock_call_fn mock_call_; 171 impl_fn generate_impl_fn_; 172 173 template <typename> 174 friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock 175 friend class ::absl::random_internal::MockHelpers; // for InvokeMock 176 }; 177 178 ABSL_NAMESPACE_END 179 } // namespace absl 180 181 #endif // ABSL_RANDOM_BIT_GEN_REF_H_ 182