• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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