userver: userver/utils/rand.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
rand.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/rand.hpp
4/// @brief Random number generators
5/// @ingroup userver_universal
6
7#include <algorithm>
8#include <cstdint>
9#include <limits>
10#include <random>
11#include <utility>
12
13#include <userver/compiler/thread_local.hpp>
14#include <userver/utils/assert.hpp>
15
16USERVER_NAMESPACE_BEGIN
17
18namespace utils {
19
20/// @brief Virtualized standard UniformRandomBitGenerator concept, for use
21/// with random number distributions
23 public:
24 using result_type = uint32_t;
25
26 virtual ~RandomBase() = default;
27
28 virtual result_type operator()() = 0;
29
30 static constexpr result_type min() { return std::mt19937::min(); }
31 static constexpr result_type max() { return std::mt19937::max(); }
32};
33
34namespace impl {
35
36std::seed_seq MakeSeedSeq();
37
38class RandomImpl final : public RandomBase {
39 public:
40 RandomImpl();
41
42 result_type operator()() override { return gen_(); }
43
44 private:
45 std::mt19937 gen_;
46};
47
48compiler::ThreadLocalScope<RandomImpl> UseLocalRandomImpl();
49
50} // namespace impl
51
52/// @brief Calls @a func with a thread-local UniformRandomBitGenerator
53/// (specifically of type utils::RandomBase).
54///
55/// @note The provided `RandomBase` instance is not cryptographically secure.
56///
57/// @a func should not contain any task context switches. That is, it should not
58/// use any userver synchronization primitives and should not access
59/// userver-based web or database clients.
60///
61/// @a func should not store the reference to the provided thread-local variable
62/// for use outside of the WithThreadLocal scope.
63///
64/// Prefer utils::RandRange if possible.
65///
66/// ## Usage example
67///
68/// Standard distributions can be passed to WithDefaultRandom directly:
69/// @snippet utils/rand_test.cpp WithDefaultRandom distribution
70///
71/// A lambda can be passed to perform a series of operations more efficiently:
72/// @snippet utils/rand_test.cpp WithDefaultRandom multiple
73///
74/// @param func functor that will be invoked with the RNG
75/// @returns The invocation result of @a func
76template <typename Func>
77decltype(auto) WithDefaultRandom(Func&& func) {
78 auto random = impl::UseLocalRandomImpl();
79 return std::forward<Func>(func)(static_cast<RandomBase&>(*random));
80}
81
82/// @brief Generates a random number in range [from, to)
83/// @note The used random generator is not cryptographically secure
84/// @note `from_inclusive` must be less than `to_exclusive`
85template <typename T>
86T RandRange(T from_inclusive, T to_exclusive) {
87 UINVARIANT(from_inclusive < to_exclusive,
88 "attempt to get a random value in an incorrect range");
89 if constexpr (std::is_floating_point_v<T>) {
90 return utils::WithDefaultRandom(
91 std::uniform_real_distribution<T>{from_inclusive, to_exclusive});
92 } else {
93 // 8-bit types are not allowed in uniform_int_distribution, so increase the
94 // T size.
95 return utils::WithDefaultRandom(
96 std::uniform_int_distribution<std::common_type_t<T, unsigned short>>{
97 from_inclusive, to_exclusive - 1});
98 }
99}
100
101/// @brief Generates a random number in range [0, to)
102/// @note The used random generator is not cryptographically secure
103template <typename T>
104T RandRange(T to_exclusive) {
105 return RandRange(T{0}, to_exclusive);
106}
107
108/// @brief Shuffles the elements within the container
109/// @note The method used for determining a random permutation is not
110/// cryptographically secure
111template <typename Container>
112void Shuffle(Container& container) {
113 utils::WithDefaultRandom([&container](RandomBase& rng) {
114 std::shuffle(std::begin(container), std::end(container), rng);
115 });
116}
117
118/// @brief Generate a random number in the whole `uint32_t` range
119/// @note The used random generator is not cryptographically secure
120/// @warning Don't use `Rand() % N`, use `RandRange` instead
121std::uint32_t Rand();
122
123} // namespace utils
124
125USERVER_NAMESPACE_END