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