|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- //
- // Copyright 2018 The Abseil Authors.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // https://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- // -----------------------------------------------------------------------------
- // File: bit_gen_ref.h
- // -----------------------------------------------------------------------------
- //
- // This header defines a bit generator "reference" class, for use in interfaces
- // that take both Abseil (e.g. `absl::BitGen`) and standard library (e.g.
- // `std::mt19937`) bit generators.
-
- #ifndef ABSL_RANDOM_BIT_GEN_REF_H_
- #define ABSL_RANDOM_BIT_GEN_REF_H_
-
- #include <limits>
- #include <type_traits>
- #include <utility>
-
- #include "absl/base/internal/fast_type_id.h"
- #include "absl/base/macros.h"
- #include "absl/meta/type_traits.h"
- #include "absl/random/internal/distribution_caller.h"
- #include "absl/random/internal/fast_uniform_bits.h"
-
- namespace absl
- {
- ABSL_NAMESPACE_BEGIN
- namespace random_internal
- {
-
- template<typename URBG, typename = void, typename = void, typename = void>
- struct is_urbg : std::false_type
- {
- };
-
- template<typename URBG>
- struct is_urbg<
- URBG,
- absl::enable_if_t<std::is_same<
- typename URBG::result_type,
- typename std::decay<decltype((URBG::min)())>::type>::value>,
- absl::enable_if_t<std::is_same<
- typename URBG::result_type,
- typename std::decay<decltype((URBG::max)())>::type>::value>,
- absl::enable_if_t<std::is_same<
- typename URBG::result_type,
- typename std::decay<decltype(std::declval<URBG>()())>::type>::value>> : std::true_type
- {
- };
-
- template<typename>
- struct DistributionCaller;
- class MockHelpers;
-
- } // namespace random_internal
-
- // -----------------------------------------------------------------------------
- // absl::BitGenRef
- // -----------------------------------------------------------------------------
- //
- // `absl::BitGenRef` is a type-erasing class that provides a generator-agnostic
- // non-owning "reference" interface for use in place of any specific uniform
- // random bit generator (URBG). This class may be used for both Abseil
- // (e.g. `absl::BitGen`, `absl::InsecureBitGen`) and Standard library (e.g
- // `std::mt19937`, `std::minstd_rand`) bit generators.
- //
- // Like other reference classes, `absl::BitGenRef` does not own the
- // underlying bit generator, and the underlying instance must outlive the
- // `absl::BitGenRef`.
- //
- // `absl::BitGenRef` is particularly useful when used with an
- // `absl::MockingBitGen` to test specific paths in functions which use random
- // values.
- //
- // Example:
- // void TakesBitGenRef(absl::BitGenRef gen) {
- // int x = absl::Uniform<int>(gen, 0, 1000);
- // }
- //
- class BitGenRef
- {
- // SFINAE to detect whether the URBG type includes a member matching
- // bool InvokeMock(base_internal::FastTypeIdType, void*, void*).
- //
- // These live inside BitGenRef so that they have friend access
- // to MockingBitGen. (see similar methods in DistributionCaller).
- template<template<class...> class Trait, class AlwaysVoid, class... Args>
- struct detector : std::false_type
- {
- };
- template<template<class...> class Trait, class... Args>
- struct detector<Trait, absl::void_t<Trait<Args...>>, Args...> : std::true_type
- {
- };
-
- template<class T>
- using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
- std::declval<base_internal::FastTypeIdType>(), std::declval<void*>(), std::declval<void*>()
- ));
-
- template<typename T>
- using HasInvokeMock = typename detector<invoke_mock_t, void, T>::type;
-
- public:
- BitGenRef(const BitGenRef&) = default;
- BitGenRef(BitGenRef&&) = default;
- BitGenRef& operator=(const BitGenRef&) = default;
- BitGenRef& operator=(BitGenRef&&) = default;
-
- template<typename URBG, typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value && random_internal::is_urbg<URBG>::value && !HasInvokeMock<URBG>::value)>* = nullptr>
- BitGenRef(URBG& gen) // NOLINT
- :
- t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
- mock_call_(NotAMock),
- generate_impl_fn_(ImplFn<URBG>)
- {
- }
-
- template<typename URBG, typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value && random_internal::is_urbg<URBG>::value && HasInvokeMock<URBG>::value)>* = nullptr>
- BitGenRef(URBG& gen) // NOLINT
- :
- t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
- mock_call_(&MockCall<URBG>),
- generate_impl_fn_(ImplFn<URBG>)
- {
- }
-
- using result_type = uint64_t;
-
- static constexpr result_type(min)()
- {
- return (std::numeric_limits<result_type>::min)();
- }
-
- static constexpr result_type(max)()
- {
- return (std::numeric_limits<result_type>::max)();
- }
-
- result_type operator()()
- {
- return generate_impl_fn_(t_erased_gen_ptr_);
- }
-
- private:
- using impl_fn = result_type (*)(uintptr_t);
- using mock_call_fn = bool (*)(uintptr_t, base_internal::FastTypeIdType, void*, void*);
-
- template<typename URBG>
- static result_type ImplFn(uintptr_t ptr)
- {
- // Ensure that the return values from operator() fill the entire
- // range promised by result_type, min() and max().
- absl::random_internal::FastUniformBits<result_type> fast_uniform_bits;
- return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr));
- }
-
- // Get a type-erased InvokeMock pointer.
- template<typename URBG>
- static bool MockCall(uintptr_t gen_ptr, base_internal::FastTypeIdType type, void* result, void* arg_tuple)
- {
- return reinterpret_cast<URBG*>(gen_ptr)->InvokeMock(type, result, arg_tuple);
- }
- static bool NotAMock(uintptr_t, base_internal::FastTypeIdType, void*, void*)
- {
- return false;
- }
-
- inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple, void* result)
- {
- if (mock_call_ == NotAMock)
- return false; // avoids an indirect call.
- return mock_call_(t_erased_gen_ptr_, type, args_tuple, result);
- }
-
- uintptr_t t_erased_gen_ptr_;
- mock_call_fn mock_call_;
- impl_fn generate_impl_fn_;
-
- template<typename>
- friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock
- friend class ::absl::random_internal::MockHelpers; // for InvokeMock
- };
-
- ABSL_NAMESPACE_END
- } // namespace absl
-
- #endif // ABSL_RANDOM_BIT_GEN_REF_H_
|