-
Notifications
You must be signed in to change notification settings - Fork 1.6k
<random>
: Fuse TR1 types into Standard types
#5712
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
StephanTLavavej
merged 13 commits into
microsoft:main
from
StephanTLavavej:nuclear-fusion
Sep 10, 2025
Merged
<random>
: Fuse TR1 types into Standard types
#5712
StephanTLavavej
merged 13 commits into
microsoft:main
from
StephanTLavavej:nuclear-fusion
Sep 10, 2025
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
…ied. Manually tested: ``` template <class T, unsigned long long M> constexpr int old_get_wc() noexcept { int K; if constexpr (M == 0) { K = (8 * sizeof(T) + 31) / 32; } else { unsigned long long Val = 1ULL << 32; for (K = 1; 0 < Val && Val < M; ++K) { Val = Val << 32; } } return K; } template <class T, unsigned long long M> constexpr int new_get_wc() noexcept { if constexpr (M == 0) { return (8 * sizeof(T) + 31) / 32; } else if constexpr (M > (1ULL << 32)) { return 2; } else { return 1; } } template <unsigned long long M> constexpr bool test() { return old_get_wc<unsigned int, M>() == new_get_wc<unsigned int, M>(); } static_assert(test<0>()); static_assert(test<1>()); static_assert(test<2>()); static_assert(test<(1ULL << 31)>()); static_assert(test<(1ULL << 32) - 2>()); static_assert(test<(1ULL << 32) - 1>()); static_assert(test<(1ULL << 32)>()); static_assert(test<(1ULL << 32) + 1>()); static_assert(test<(1ULL << 32) + 2>()); static_assert(test<(1ULL << 33)>()); static_assert(test<(1ULL << 63)>()); static_assert(test<0xFFFF'FFFF'FFFF'FFFFULL>()); C:\Temp>cl /EHsc /nologo /W4 /std:c++latest /c meow.cpp meow.cpp C:\Temp> ```
We no longer need to suppress the deprecation warning. We inherit directly from _Swc_base now. (After TR1 removal, we can fuse that away too, which will be a much larger simplification.) Drop a pointless comment. We now directly say that _Traits is an _Swc_traits. subtract_with_carry's default constructor passed 0u to _Swc_base's constructor, so we do that. Cleanup: Simplify operator!=().
We no longer need to suppress the deprecation warning. We inherit directly from _Circ_buf now. That inheritance continues to be private, whereas mersenne_twister inherits from _Circ_buf publicly. We no longer need a _Mybase alias. When constructing `mersenne_twister_engine(_Seed_seq& _Seq)`, avoid redundantly calling `seed(default_seed)` before `seed(_Seq)`. `_WMSK` is a direct member now. Cleanup: Simplify operator!=(). Note that operator>>() for mersenne_twister calls `_Eng.seed(_Gen)` which is radically different from mersenne_twister_engine's `seed(_Seed_seq& _Seq)`. Accordingly I've inlined the old implementation (which directly loads the serialized values). operator<<() for mersenne_twister uses mersenne_twister::state_size as a bound. This is just _Nx, which I've chosen to use for increased symmetry with the other operator. _Refill_lower() etc. are private (same as before when they were inherited privately).
…that the data member is retained for bincompat.
This was very close to the finish line. Instead of switching between the old discard_block and the newer _Discard_block_base (introduced by GH 4066), simply make _Nx's type vary. Introduce _Block_type (commented with TRANSITION, ABI) and static_cast to avoid sign-comparison warnings, which was previously achieved by switching between the base classes. We know the constants are within range, so this is value-preserving. As usual, we no longer need to suppress the deprecation warning.
This is a separate commit to improve the previous commit's diff.
_Eval can always use _Rng_from_urng_v2 for Standard engines. Make equality slightly more efficient in debug mode.
Make equality slightly more efficient in debug mode. Note that _Read and _Write are public in uniform_real, but remain private in uniform_real_distribution.
Dev09_181509_tr1_inf_loop_uniform_int_ull: Update comment to describe the Standard machinery being tested. (I know the test name is old.) Wacky min()/max() needs to be constexpr. P0952R2_new_generate_canonical: These tests are comparing tr1 and Standard generators. As they aren't testing absolute behavior, just identical behavior, they're meaningless when the tr1 generators are missing. VSO_0000000_instantiate_iterators_misc: The linear_congruential type being formed is effectively equivalent to minstd_rand0 which is already covered. Now guard the tr1_engine_test_impl() helper. Update comments for searchability. tests/tr1: Switch between tr1 and Standard types. This will continue to serve as a minimal check that the tr1 types keep working until they're ultimately removed soon. tr1/tests/random1 has some coverage for old interfaces that don't apply to the Standard types. We also need to test mersenne_twister_engine's differently-named constants. We need static_cast<int> to avoid sign comparison warnings with discard_block_engine. tr1/tests/random2 has to handle the Standard's different default max for uniform_int_distribution.
AraHaan
reviewed
Sep 8, 2025
I'm mirroring this to the MSVC-internal repo - please notify me if any further changes are pushed |
zacklj89
approved these changes
Sep 10, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Over the years, we've minimized the amount of TR1 legacy machinery that we emit in modern modes (C++17 and later). The last headache has been how various
<random>
engines and distributions were implemented by deriving from their earlier TR1 incarnations. This PR fuses the TR1 machinery into the Standard types, so that the inheritance is no longer necessary, and the TR1 types can now be guarded along with the rest. This also makes the Standard types significantly easier to understand.Importantly, this doesn't affect runtime behavior or layout, so the change preserves bincompat. I verified this with
sizeof
,alignof
, and our most powerful tool/d1reportSingleClassLayout
. This of course has source-breaking impact if someone was referring to the TR1 types in modern modes (and had disregarded our deprecation warnings), with upgrading being fairly easy. There is also minor source-breaking impact for obscure scenarios (e.g. someone giving a pre-Standard engine to a Standard distribution, which happened in one of my old tests being updated here).This works towards the complete removal of TR1, which I'll do in a followup PR.
⚙️ Commits
Click to expand commit descriptions:
&arr[size]
insubtract_with_carry_engine/mersenne_twister_engine::seed()
._Kx
to be local tosubtract_with_carry_engine::seed()
._Swc_traits::_Get_wc()
to be private, constexpr, and simplified.subtract_with_carry
intosubtract_with_carry_engine
._Swc_base
now. (After TR1 removal, we can fuse that away too, which will be a much larger simplification.)_Traits
is an_Swc_traits
.subtract_with_carry
's default constructor passed0u
to_Swc_base
's constructor, so we do that.operator!=()
.subtract_with_carry_engine
machinery private.mersenne_twister
intomersenne_twister_engine
._Circ_buf
now. That inheritance continues to be private, whereasmersenne_twister
inherits from_Circ_buf
publicly._Mybase
alias.mersenne_twister_engine(_Seed_seq& _Seq)
, avoid redundantly callingseed(default_seed)
beforeseed(_Seq)
._WMSK
is a direct member now.operator!=()
.operator>>()
formersenne_twister
calls_Eng.seed(_Gen)
which is radically different frommersenne_twister_engine
'sseed(_Seed_seq& _Seq)
. Accordingly I've inlined the old implementation (which directly loads the serialized values).operator<<()
formersenne_twister
usesmersenne_twister::state_size
as a bound. This is just_Nx
, which I've chosen to use for increased symmetry with the other operator._Refill_lower()
etc. are private (same as before when they were inherited privately).mersenne_twister_engine
machinery private.mersenne_twister_engine: _Dxval
is always_Dx
; hardcode it, and note that the data member is retained for bincompat._Discard_block_base
intodiscard_block_engine
.discard_block
and the newer_Discard_block_base
(introduced by Implement LWG-3561 Issue with internal counter indiscard_block_engine
#4066), simply make_Nx
's type vary._Block_type
(commented with TRANSITION, ABI) andstatic_cast
to avoid sign-comparison warnings,which was previously achieved by switching between the base classes. We know the constants are within range, so this is value-preserving.
_Discard_block_base
.uniform_int
intouniform_int_distribution
._Eval
can always use_Rng_from_urng_v2
for Standard engines.uniform_real
intouniform_real_distribution
._Read
and_Write
are public inuniform_real
, but remain private inuniform_real_distribution
._HAS_TR1_NAMESPACE
.Dev09_181509_tr1_inf_loop_uniform_int_ull
: Update comment to describe the Standard machinery being tested. (I know the test name is old.)Wacky
min()/max()
needs to be constexpr.P0952R2_new_generate_canonical
: These tests are comparing tr1 and Standard generators. As they aren't testing absolute behavior, just identical behavior, they're meaningless when the tr1 generators are missing.VSO_0000000_instantiate_iterators_misc
: Thelinear_congruential
type being formed is effectively equivalent tominstd_rand0
which is already covered. Now guard thetr1_engine_test_impl()
helper. Update comments for searchability.tests/tr1
: Switch between tr1 and Standard types. This will continue to serve as a minimal check that the tr1 types keep working until they're ultimately removed soon.tr1/tests/random1
has some coverage for old interfaces that don't apply to the Standard types. We also need to testmersenne_twister_engine
's differently-named constants. We needstatic_cast<int>
to avoid sign comparison warnings withdiscard_block_engine
.tr1/tests/random2
has to handle the Standard's different default max foruniform_int_distribution
.🧑🔬
_Swc_traits::_Get_wc()
TestingClick to expand science:
🕵️ Layout Test Case
Click to expand layout test case:
📚 Layout Results
Click to expand old x64:
Click to expand new x64:
Click to expand old x86:
Click to expand new x86:
🔀 Diff Layouts
Diff the layouts in VSCode to see the base classes vanishing without affecting the positions of data members. Thanks @MattStephanson for noticing that I initially forgot to inspect
BigDiscard
.