KEMBAR78
Remove non-Standard TR1 by StephanTLavavej · Pull Request #5763 · microsoft/STL · GitHub
Skip to content

Conversation

StephanTLavavej
Copy link
Member

Overview

This permanently removes our non-Standard TR1 implementation, which was superseded by C++11.

Fixes #183. Fixes #1002. (And DevCom-381099 / Microsoft-internal VSO-729899.)

The std::tr1 namespace mostly consisted of aliases for Standard machinery. For example, std::tr1::shared_ptr was just an alias for std::shared_ptr. One member function was affected: in <array>, TR1 array::assign() was superseded by Standard array::fill(). The most significant differences were in <random>, where the old engines and distributions were superseded by ones with new names and superior interfaces. For example, TR1 mersenne_twister and uniform_int were superseded by Standard mersenne_twister_engine and uniform_int_distribution.

Eliminating TR1 removes unnecessary complexity from this fiendishly complex library. For users, it removes ways to write non-portable code. For library implementers, it reduces our maintenance burden, making it easier for us to reason about the remaining Standard code. We've been working towards TR1 removal for a long time:

In Aug 2017, VS 2017 15.3 shipped, removing most of TR1's machinery by default in C++17 mode and later.

In Dec 2017, VS 2017 15.5 shipped a warning to users that "The non-Standard std::tr1 namespace and TR1-only machinery are deprecated and will be REMOVED."

In May 2024, VS 2022 17.10 shipped #4284, which extended the deprecation warnings to the non-Standard TR1 engines and distributions in <random> that were also available in the std namespace because they served as base classes for the Standard engines and distributions.

Very recently, my #5712 disconnected the Standard engines and distributions from their TR1 ancestors, making it possible to finally remove TR1. And now, the conclusion.

In addition to removing the std::tr1 namespace, the old array member function, and the old engines and distributions lurking in the std namespace, this makes two more significant simplifications possible. Because the old engines had a different interface, various algorithms had to accept either old or new engines. We can now drop that logic, always using the more efficient new interface. Also, subtract_with_carry_engine had a complicated implementation, which was previously tangled up with somewhat different TR1 engines that had been abandoned during Standardization. Following up on #5712, I can fuse another base class, and a traits class, away. (This is the bulk of the commits and complexity in this PR.)

Commits

  • Require Standard URBGs, delete machinery for TR1 generators.
  • Remove _HAS_TR1_NAMESPACE.
  • Remove _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING.
  • Remove _SILENCE_TR1_RANDOM_DEPRECATION_WARNING.
  • Simplify test for removed non-Standard names.
  • Drop _Swc_base operator>>() and operator<<(), and _Swc_traits::_Write().
    • They were unused by subtract_with_carry_engine.
  • Drop _Swc_base min() and max(), and _Swc_traits::_Max.
    • They were unused by subtract_with_carry_engine.
  • Fuse _Swc_base operator()() with strengthened noexcept.
  • Fuse _Swc_base discard().
  • Fuse _Swc_base operator==().
    • We don't need to static_cast when calling _Left._Equals(_Right).
  • Fuse _Swc_base default_seed.
  • Introduce and consistently use _Mytraits in _Swc_base and subtract_with_carry_engine to make fusion easier and avoid shadowing.
  • Fuse _Swc_base _Write_full().
  • Drop _Swc_base default ctor and ctor/seed() from _Gen&.
    • They were unused.
  • Fuse _Swc_base construction.
    • Avoid redundant seeding in subtract_with_carry_engine(_Seed_seq&).
  • Fuse _Swc_base seed().
    • _Readcy was always false.
  • Finish fusing _Swc_base.
    • Note that _Setx and _Carry are now directly private; they were previously inherited privately.
  • _Carry is now a direct data member.
  • Simplify _Swc_traits::_Reset() as _Readcy is always false.
  • Fuse _Swc_traits _Cy and _Mod, avoiding shadowing in _Write_full()!
    • Also drop now-unused _Mod_t and _Seed_t.
  • Fuse _Swc_traits _Write_full().
  • Finish fusing _Swc_traits.
    • _Reset() and _Reduce() are becoming non-static member functions.
  • Fuse _Reset() into seed().
  • Consistently use %=.

subtract_with_carry_engine Representation

I verified that subtract_with_carry_engine's representation isn't being changed by removing _Swc_base inheritance. I checked 32-bit and 64-bit engines, on both x64 and x86. I compared against the MSVC Build Tools 14.50 Preview 1, so the differences here also include #5712's recent removal of TR1 subtract_with_carry inheritance.

Click to expand test case and output:

Test Case

C:\Temp>type meow.cpp
#include <print>
#include <random>
using namespace std;

struct Test1 : ranlux24_base {};
struct Test2 : ranlux48_base {};

int main() {
    println("Arch: {}-bit; _MSVC_STL_UPDATE: {}", sizeof(void*) * 8, _MSVC_STL_UPDATE);
    println("ranlux24_base: sizeof: {}; alignof: {}", sizeof(ranlux24_base), alignof(ranlux24_base));
    println("ranlux48_base: sizeof: {}; alignof: {}", sizeof(ranlux48_base), alignof(ranlux48_base));
}
C:\Temp>cl /EHsc /nologo /W4 /std:c++latest /MT /Od /d1reportSingleClassLayoutTest meow.cpp && meow
meow.cpp

Old x64

class Test1     size(200):
        +---
 0      | +--- (base class std::subtract_with_carry_engine<unsigned int,24,10,24>)
 0      | | +--- (base class std::subtract_with_carry<unsigned int,16777216,10,24>)
 0      | | | +--- (base class std::_Swc_base<unsigned int,10,24,struct std::_Swc_traits<unsigned int,16777216,24> >)
 0      | | | | +--- (base class std::_Circ_buf<unsigned int,24>)
 0      | | | | | _Idx
 4      | | | | | _Ax
        | | | | +---
196     | | | | _Carry
        | | | +---
        | | +---
        | +---
        +---

class Test2     size(208):
        +---
 0      | +--- (base class std::subtract_with_carry_engine<unsigned __int64,48,5,12>)
 0      | | +--- (base class std::subtract_with_carry<unsigned __int64,281474976710656,5,12>)
 0      | | | +--- (base class std::_Swc_base<unsigned __int64,5,12,struct std::_Swc_traits<unsigned __int64,281474976710656,12> >)
 0      | | | | +--- (base class std::_Circ_buf<unsigned __int64,12>)
 0      | | | | | _Idx
        | | | | | <alignment member> (size=4)
 8      | | | | | _Ax
        | | | | +---
200     | | | | _Carry
        | | | | <alignment member> (size=4)
        | | | +---
        | | +---
        | +---
        +---
Arch: 64-bit; _MSVC_STL_UPDATE: 202507
ranlux24_base: sizeof: 200; alignof: 4
ranlux48_base: sizeof: 208; alignof: 8

New x64

class Test1     size(200):
        +---
 0      | +--- (base class std::subtract_with_carry_engine<unsigned int,24,10,24>)
 0      | | +--- (base class std::_Circ_buf<unsigned int,24>)
 0      | | | _Idx
 4      | | | _Ax
        | | +---
196     | | _Carry
        | +---
        +---

class Test2     size(208):
        +---
 0      | +--- (base class std::subtract_with_carry_engine<unsigned __int64,48,5,12>)
 0      | | +--- (base class std::_Circ_buf<unsigned __int64,12>)
 0      | | | _Idx
        | | | <alignment member> (size=4)
 8      | | | _Ax
        | | +---
200     | | _Carry
        | | <alignment member> (size=4)
        | +---
        +---
Arch: 64-bit; _MSVC_STL_UPDATE: 202510
ranlux24_base: sizeof: 200; alignof: 4
ranlux48_base: sizeof: 208; alignof: 8

Old x86

class Test1     size(200):
        +---
 0      | +--- (base class std::subtract_with_carry_engine<unsigned int,24,10,24>)
 0      | | +--- (base class std::subtract_with_carry<unsigned int,16777216,10,24>)
 0      | | | +--- (base class std::_Swc_base<unsigned int,10,24,struct std::_Swc_traits<unsigned int,16777216,24> >)
 0      | | | | +--- (base class std::_Circ_buf<unsigned int,24>)
 0      | | | | | _Idx
 4      | | | | | _Ax
        | | | | +---
196     | | | | _Carry
        | | | +---
        | | +---
        | +---
        +---

class Test2     size(208):
        +---
 0      | +--- (base class std::subtract_with_carry_engine<unsigned __int64,48,5,12>)
 0      | | +--- (base class std::subtract_with_carry<unsigned __int64,281474976710656,5,12>)
 0      | | | +--- (base class std::_Swc_base<unsigned __int64,5,12,struct std::_Swc_traits<unsigned __int64,281474976710656,12> >)
 0      | | | | +--- (base class std::_Circ_buf<unsigned __int64,12>)
 0      | | | | | _Idx
        | | | | | <alignment member> (size=4)
 8      | | | | | _Ax
        | | | | +---
200     | | | | _Carry
        | | | | <alignment member> (size=4)
        | | | +---
        | | +---
        | +---
        +---
Arch: 32-bit; _MSVC_STL_UPDATE: 202507
ranlux24_base: sizeof: 200; alignof: 4
ranlux48_base: sizeof: 208; alignof: 8

New x86

class Test1     size(200):
        +---
 0      | +--- (base class std::subtract_with_carry_engine<unsigned int,24,10,24>)
 0      | | +--- (base class std::_Circ_buf<unsigned int,24>)
 0      | | | _Idx
 4      | | | _Ax
        | | +---
196     | | _Carry
        | +---
        +---

class Test2     size(208):
        +---
 0      | +--- (base class std::subtract_with_carry_engine<unsigned __int64,48,5,12>)
 0      | | +--- (base class std::_Circ_buf<unsigned __int64,12>)
 0      | | | _Idx
        | | | <alignment member> (size=4)
 8      | | | _Ax
        | | +---
200     | | _Carry
        | | <alignment member> (size=4)
        | +---
        +---
Arch: 32-bit; _MSVC_STL_UPDATE: 202510
ranlux24_base: sizeof: 200; alignof: 4
ranlux48_base: sizeof: 208; alignof: 8

…:_Write()`.

They were unused by `subtract_with_carry_engine`.
They were unused by `subtract_with_carry_engine`.
We don't need to `static_cast` when calling `_Left._Equals(_Right)`.
…ct_with_carry_engine` to make fusion easier and avoid shadowing.
Avoid redundant seeding in `subtract_with_carry_engine(_Seed_seq&)`.
`_Readcy` was always `false`.
Note that `_Setx` and `_Carry` are now directly `private`; they were previously inherited privately.
…ll()`!

Also drop now-unused `_Mod_t` and `_Seed_t`.
`_Reset()` and `_Reduce()` are becoming non-static member functions.
@StephanTLavavej
Copy link
Member Author

I'm mirroring this to the MSVC-internal repo - please notify me if any further changes are pushed.

Copy link
Member

@davidmrdavid davidmrdavid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just a question and a non-blocking ultra-nit suggestion
Also:

image

👏 👏 👏

#define xtime delete
#define xtime_get delete

#define discard_block delete
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a new trick to me: what's the purpose of these macros mapping to delete (and previously to known constants)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It ensures that if we ever mention them in our headers, the test will catastrophically fail to compile.

The #define meow 1729 ones are for when we want to mention them (because they're non-Standard extensions), but need to push-undef-pop, and at the end we check that the specific macro value has been restored.

This test is probably overkill but it's cheap to maintain.

@StephanTLavavej StephanTLavavej moved this from Final Review to Merging in STL Code Reviews Oct 9, 2025
@StephanTLavavej StephanTLavavej merged commit 7d6e34a into microsoft:main Oct 10, 2025
39 checks passed
@github-project-automation github-project-automation bot moved this from Merging to Done in STL Code Reviews Oct 10, 2025
@StephanTLavavej StephanTLavavej deleted the tr-none branch October 10, 2025 10:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Something can be improved

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

<random>: Still provides the uniform_int and uniform_real class templates <random>: Remove TR1 machinery

2 participants