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