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