Open-source project
ilqvya/random avatar
ilqvya/random

ilqvya/random: one header file for random numbers in modern C++

Random for modern C++ with convenient API

953 stars83 forksC++MIT

At a glance

What is it?
ilqvya/random is a header-only C++ library whose whole API is a Random::get call, with seeded engines, weighted values, sampling without replacement and C++20 module support. It is written in C++11 but reaches for std::philox4x64 when you compile it as C++26.
Who is it for?
Adopt ilqvya/random when you are writing C++ and want random values without managing an engine and a distribution, especially in game logic, procedural generation, tests and simulations where the call site should read like a value rather than a pipeline. Do not adopt it when you need cryptographic randomness, since the engines are the standard pseudo-random ones, or when your project standard is older than C++11, which the compiler support list excludes.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The whole API is one get call

The library's argument for existing is a contrast between three styles of random number handling in C++, and the third one is the pitch.

The C style requires seeding by hand and writing your own distribution:

cpp
  srand( time(NULL) ); // seed with time since epoch
  auto random_number = (rand() % 9) + 1; // get a pseudo-random integer between 1 and 9

The C++11 style still asks for a manual seed and adds object management:

cpp
  std::random_device random_device; // create object for seeding
  std::mt19937 engine{random_device()}; // create engine and seed it
  std::uniform_int_distribution<> dist(1,9); // create distribution for integers with [1; 9] range
  auto random_number = dist(engine); // finally get a pseudo-random integer number

The library's own style is the call you would want to write:

cpp
  // auto seeded
  auto random_number = Random::get(1, 9); // invoke 'get' method to generate a pseudo-random integer in [1; 9] range
  // yep, that's all.

The listed problems with the first two styles are that you must specify a seed manually, that you must write or choose your own distribution algorithm, that there are no guarantees about the quality of the random sequence produced by the C rand function, and that the C++11 route requires choosing, creating and managing a chain of objects. The mt19937 engine is called out specifically for using roughly 5000 bytes of internal state per instance, which hurts when engines are created repeatedly. This library's claim is that the engine is created and seeded for you.

Three generator flavours with different threading behaviour

The library offers three generator types, and the difference between them is about state and thread safety rather than about distribution.

random, also spelled random_thread_local, has static methods and thread_local internal state, and the README calls it a fully thread-safe default. random_static has static methods and static internal state, and is described as fast but not thread-safe across concurrent threads. random_local has non-static member methods and local member state, and can be created on the stack at local scope.

That taxonomy is more useful than it first appears. A game server or a simulation with worker threads that each need their own stream wants random_local, because the state belongs to the object and travels with it. A single-threaded tool that wants speed can take random_static. A library function that may be called from anywhere wants random, and gets the thread_local variant by default so that two threads never share a stream.

The README also documents thread local random and local random as separate sections alongside the operator-callable generator, and documents a call operator so the generator can be used as a functor.

For an adopter, the practical point is that the choice is explicit rather than hidden. There is a default, and there are two documented ways to opt out of it for a reason.

Compile-time safety for the mistakes that dangling iterators cause

The compile-time safety claims are the part of this library that goes beyond convenience, and they target a specific class of bug.

Temporary rvalue containers and arrays passed to iterator-returning overloads are deleted at compile time, which prevents the caller from getting a dangling iterator out of a temporary. That is the classic trap: you call something like a pick-from-container overload with a temporary vector, you get an iterator back, and the vector is gone before you dereference it.

Two smaller rules come with it. String literals return by value without the null terminator, so a caller is not handed a pointer into a literal with a length that disagrees with the string. And empty array access is caught at compile time rather than at runtime.

The performance claims are on the same list. The README states zero-allocation sampling with immediate return fast paths for zero-gap ranges where from equals to, container reservation through a reserve_if_supported helper, and optimal sampling algorithms without replacement.

For comparison, the standard library approach of writing the distribution and engine yourself gives you none of this: the standard distributions do not protect you from a temporary container, and the standard sampling algorithms require you to manage the output container yourself.

Integrating it: FetchContent, find_package, or a C++20 module

Integration is documented in three forms, and the recommended one is CMake FetchContent for CMake 3.11 and later:

cmake
include(FetchContent)
FetchContent_Declare(
    random
    GIT_REPOSITORY https://github.com/ilqvya/random.git
    GIT_TAG        v2.0.0
)
FetchContent_MakeAvailable(random)

The target name is random::random, and the README notes that ilqvya::random is an alternative. There is a detail worth catching: unit tests, exposed as the Random_BuildTests option, and install rules, exposed as Random_Install, are automatically disabled when the project is included via FetchContent or add_subdirectory, so vendoring the library does not drag its test suite into your build.

If you would rather install it as an external package and find it, the second form is find_package(ilqvya_random REQUIRED) followed by linking the same target names.

The third form is the C++20 module. Enable it with -DRandom_BuildModules=ON during CMake configuration, which requires CMake 3.28 or later and either the Ninja or the Visual Studio generator. You then link ilqvya::random_module and import it with import ilqvya.random;. The stated reason for the module form is zero macro leakage, clean encapsulation and faster compilation times.

There is also the manual route. The single required source file is random.hpp in the include/ilqvya directory, and the module interface ilqvya_random.cppm sits alongside it. Copy the header and you are done.

A broad feature surface beyond plain integers

The tutorial section is a long list of headings, and reading it is the fastest way to see the scope: number range, common type number range, character range, bool and coin flip, random value from std::initializer_list, random iterator, random element from array, container of random values, multi-roll and batch numbers with structured bindings, in-place fill, weighted random values, sample without replacement, choice with replacement, enum random values through C++26 reflection, shuffle with std::span views, random string, hex and UUID or GUID generation, common distributions, and then a set of sections on the engine itself including get engine, seeding, min-value, max-value, discard, is equal, serialize and deserialize.

Several of those deserve a note. Weighted random values were the headline of the v1.5.0 release, whose message mentions weighted random values and code deduplication through CRTP. Enum random values via C++26 reflection is a forward-looking feature that depends on the language version rather than on the library. The engine section is unusual for a convenience wrapper: serialization and deserialization of the engine state, discard, equality and min-value and max-value queries all expose the underlying engine rather than hiding it.

Custom distributions and custom seeders are documented as well, so the library is not a closed set: you can plug your own distribution object or your own seeding source into the same get interface.

Engine choice, seeding, and what the licence allows

The engine story has two branches, and the choice is made by the language standard you compile with rather than by configuration.

In C++26 the default engine is std::philox4x64, described as counter-based random number generation with minimal 32-byte state and full 256-bit entropy seeding. In C++11 through C++23 the library falls back to std::mt19937.

That fallback is worth understanding, because the README's own criticism of the C++11 style included mt19937's roughly 5000 bytes of state. The answer is that the library manages a single engine rather than one per call site, so the state cost is paid once instead of repeatedly. The 32-byte counter-based state is an improvement on top of that, but only for people who can compile as C++26.

Seeding is handled for you in the default path, which is the point of the auto-seeded call shown earlier, while the seeding section and the custom seeder section exist for people who need reproducibility, which is the case in tests and in procedural generation.

The licence is MIT, with the licence text in the LICENSE.MIT file at the repository root. For a single-header library that you can also vendor as one file, that is the simplest possible licensing story.

On activity, the repository is not archived and the last push was on 2026-10-19, with v2.0.0 released on 2026-10-11. The release before that was v1.5.0 on 2023-07-05 and v1.4.1 on 2022-03-27, so the gap between 1.5 and 2.0 covered a long period of little tagged release activity. The compiler support list is wide: GCC 4.9 and later, Clang 3.7 and later, and Microsoft Visual C++ 2015 and later.

Editorial conclusion

Adopt ilqvya/random when you are writing C++ and want random values without managing an engine and a distribution, especially in game logic, procedural generation, tests and simulations where the call site should read like a value rather than a pipeline. Do not adopt it when you need cryptographic randomness, since the engines are the standard pseudo-random ones, or when your project standard is older than C++11, which the compiler support list excludes. Verify first which generator flavour suits your threading model, because random_thread_local is fully thread-safe by default while random_static is fast but not thread-safe across concurrent threads, and check the version you vendor, since v2.0.0 shipped on 2026-10-11 and the previous release was v1.5.0 in July 2023.

Frequently asked questions

How do I generate a random number with ilqvya/random?

Call the static get method on the Random generator, for example Random::get(1, 9) for an integer in the inclusive range from 1 to 9. The generator is auto-seeded, so no engine or distribution has to be created first.

How do I add ilqvya/random to a CMake project?

Use FetchContent for CMake 3.11 or later, declaring the repository and the tag v2.0.0, then link the random::random or ilqvya::random target. Unit tests and install rules are disabled automatically when the project is included that way.

Is ilqvya/random thread-safe?

The default random generator, also available as random_thread_local, uses thread_local internal state and is described as a fully thread-safe default. The random_static variant is faster but not thread-safe across concurrent threads, and random_local keeps state in a member object you create on the stack.

Which engine does ilqvya/random use?

In C++26 the default engine is std::philox4x64 with 32-byte state and 256-bit entropy seeding. When compiling as C++11 through C++23 the library falls back to std::mt19937.

What licence is ilqvya/random released under?

MIT, with the licence text in the LICENSE.MIT file at the repository root.

Official sources

  1. ilqvya/random on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ilqvya-random.svg)](https://hysenlabs.com/projects/ilqvya-random)

Was this useful?