bit_gen_ref.h 6.7 KB

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