• 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 "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