bit_gen_ref.h 7.0 KB

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