diff --git a/stl/inc/random b/stl/inc/random index f6d03829529..1b558903ea9 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -4082,9 +4082,8 @@ public: return _Pvec; } - void _Init(bool _Renorm = true) { // initialize - size_t _Size = _Pvec.size(); - size_t _Idx; + void _Init(const bool _Renorm = true) { // initialize + const size_t _Size = _Pvec.size(); if (_Renorm) { if (_Pvec.empty()) { @@ -4092,14 +4091,14 @@ public: } else { // normalize probabilities double _Sum = 0; - for (_Idx = 0; _Idx < _Size; ++_Idx) { // sum all probabilities + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { // sum all probabilities _STL_ASSERT(0.0 <= _Pvec[_Idx], "invalid probability for discrete_distribution"); _Sum += _Pvec[_Idx]; } _STL_ASSERT(0.0 < _Sum, "invalid probability vector for discrete_distribution"); if (_Sum != 1.0) { - for (_Idx = 0; _Idx < _Size; ++_Idx) { + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { _Pvec[_Idx] /= _Sum; } } @@ -4107,7 +4106,7 @@ public: } _Pcdf.assign(1, _Pvec[0]); - for (_Idx = 1; _Idx < _Size; ++_Idx) { + for (size_t _Idx = 1; _Idx < _Size; ++_Idx) { _Pcdf.push_back(_Pvec[_Idx] + _Pcdf[_Idx - 1]); } } @@ -4175,14 +4174,23 @@ public: void reset() noexcept /* strengthened */ {} // clear internal state + // reused by piecewise_constant_distribution and piecewise_linear_distribution + template + _NODISCARD static result_type _Invoke_param_pcdf(_Engine& _Eng, const _Myvec& _Pcdf) { + const double _Px = _STD _Nrand_impl(_Eng); + const auto _First = _Pcdf.begin(); + const auto _Position = _STD lower_bound(_First, _STD _Prev_iter(_Pcdf.end()), _Px); + return static_cast(_Position - _First); + } + template _NODISCARD result_type operator()(_Engine& _Eng) _DISTRIBUTION_CONST { - return _Eval(_Eng, _Par); + return _Invoke_param_pcdf(_Eng, _Par._Pcdf); } template _NODISCARD result_type operator()(_Engine& _Eng, const param_type& _Par0) _DISTRIBUTION_CONST { - return _Eval(_Eng, _Par0); + return _Invoke_param_pcdf(_Eng, _Par0._Pcdf); } _NODISCARD friend bool operator==(const discrete_distribution& _Left, const discrete_distribution& _Right) { @@ -4207,40 +4215,26 @@ public: return _Dist._Par._Write(_Ostr); } -private: - template - result_type _Eval(_Engine& _Eng, const param_type& _Par0) const { - double _Px = _Nrand_impl(_Eng); - const auto _First = _Par0._Pcdf.begin(); - const auto _Position = _STD lower_bound(_First, _Prev_iter(_Par0._Pcdf.end()), _Px); - return static_cast(_Position - _First); - } - -public: param_type _Par; }; _EXPORT_STD template -class piecewise_constant_distribution - : public discrete_distribution { // piecewise constant floating-point distribution - // TRANSITION: unused _Mypbase subobject from base class +class piecewise_constant_distribution { // piecewise constant floating-point distribution public: _RNG_REQUIRE_REALTYPE(piecewise_constant_distribution, _Ty); - using _Mybase = discrete_distribution; - using _Mypbase = typename _Mybase::param_type; using result_type = _Ty; - struct param_type : _Mypbase { // parameter package + struct param_type { // parameter package using distribution_type = piecewise_constant_distribution; param_type() : _Bvec{0, 1} {} template - param_type(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2) : _Mypbase(_Noinit), _Bvec(_First1, _Last1) { + param_type(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2) : _Base_params(_Noinit), _Bvec(_First1, _Last1) { if (2 <= _Bvec.size()) { for (size_t _Idx = 0; _Idx < _Bvec.size() - 1; ++_Idx) { - this->_Pvec.push_back(static_cast(*_First2++)); + _Base_params._Pvec.push_back(static_cast(*_First2++)); } } else { // default construct _Bvec = {0, 1}; @@ -4250,12 +4244,12 @@ public: } template - param_type(initializer_list<_Ty> _Ilist, _Fn _Func) : _Mypbase(_Noinit) { + param_type(initializer_list<_Ty> _Ilist, _Fn _Func) : _Base_params(_Noinit) { if (2 <= _Ilist.size()) { _Bvec.assign(_Ilist); for (size_t _Idx = 0; _Idx < _Bvec.size() - 1; ++_Idx) { - this->_Pvec.push_back(_Func(_Ty{0.5} * (_Bvec[_Idx] + _Bvec[_Idx + 1]))); + _Base_params._Pvec.push_back(_Func(_Ty{0.5} * (_Bvec[_Idx] + _Bvec[_Idx + 1]))); } } else { // default construct _Bvec = {0, 1}; @@ -4265,7 +4259,7 @@ public: } template - param_type(size_t _Count, _Ty _Low, _Ty _High, _Fn _Func) : _Mypbase(_Count, _Low, _High, _Func) { + param_type(size_t _Count, _Ty _Low, _Ty _High, _Fn _Func) : _Base_params(_Count, _Low, _High, _Func) { _Ty _Range = _High - _Low; if (_Count <= 0) { _Count = 1; @@ -4278,8 +4272,7 @@ public: } _NODISCARD friend bool operator==(const param_type& _Left, const param_type& _Right) { - return static_cast(_Left) == static_cast(_Right) - && _Left._Bvec == _Right._Bvec; + return _Left._Base_params == _Right._Base_params && _Left._Bvec == _Right._Bvec; } #if !_HAS_CXX20 @@ -4295,7 +4288,7 @@ public: #pragma warning(push) #pragma warning(disable : 4244) // '%s': conversion from '%s' to '%s', possible loss of data _NODISCARD vector<_Ty> densities() const { - vector<_Ty> _Ans(this->_Pvec.begin(), this->_Pvec.end()); + vector<_Ty> _Ans(_Base_params._Pvec.begin(), _Base_params._Pvec.end()); for (size_t _Idx = 0; _Idx < _Ans.size(); ++_Idx) { _Ans[_Idx] /= _Bvec[_Idx + 1] - _Bvec[_Idx]; @@ -4306,9 +4299,10 @@ public: #pragma warning(pop) void _Init() { // initialize - _Mypbase::_Init(); + _Base_params._Init(); } + discrete_distribution::param_type _Base_params; vector<_Ty> _Bvec; }; @@ -4376,10 +4370,11 @@ public: template friend basic_istream<_Elem, _Traits>& operator>>(basic_istream<_Elem, _Traits>& _Istr, piecewise_constant_distribution& _Dist) { // read state from _Istr - static_cast(_Dist._Par)._Read(_Istr); + _Dist._Par._Base_params._Read(_Istr); _Dist._Par._Bvec.clear(); - for (size_t _Idx = _Dist._Par._Pvec.size() + 1; 0 < _Idx; --_Idx) { // get a value and add to intervals vector + for (size_t _Idx = _Dist._Par._Base_params._Pvec.size() + 1; 0 < _Idx; --_Idx) { + // get a value and add to intervals vector double _Val; _In(_Istr, _Val); _Dist._Par._Bvec.push_back(_Val); @@ -4390,7 +4385,7 @@ public: template friend basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, const piecewise_constant_distribution& _Dist) { // write state to _Ostr - static_cast(_Dist._Par)._Write(_Ostr); + _Dist._Par._Base_params._Write(_Ostr); for (const auto& _Val : _Dist._Par._Bvec) { _Out(_Ostr, _Val); @@ -4401,39 +4396,36 @@ public: template result_type _Eval(_Engine& _Eng, const param_type& _Par0) _DISTRIBUTION_CONST { - size_t _Px = _Mybase::operator()(_Eng, _Par0); + size_t _Px = discrete_distribution::_Invoke_param_pcdf(_Eng, _Par0._Base_params._Pcdf); uniform_real_distribution<_Ty> _Dist(_Par0._Bvec[_Px], _Par0._Bvec[_Px + 1]); return _Dist(_Eng); } + discrete_distribution _Unused; // TRANSITION, ABI: this was a base class subobject param_type _Par; }; _EXPORT_STD template -class piecewise_linear_distribution - : public discrete_distribution { // piecewise linear floating-point distribution - // TRANSITION: unused _Mypbase subobject from base class +class piecewise_linear_distribution { // piecewise linear floating-point distribution public: _RNG_REQUIRE_REALTYPE(piecewise_linear_distribution, _Ty); - using _Mybase = discrete_distribution; - using _Mypbase = typename _Mybase::param_type; using result_type = _Ty; - struct param_type : _Mypbase { // parameter package - // TRANSITION, ABI: stores probability densities (N + 1 elements) in _Mybase::_Pvec - // this breaks invariants of discrete_distribution::param_type + struct param_type { // parameter package using distribution_type = piecewise_linear_distribution; - param_type() : _Bvec{0, 1} { - this->_Pvec.push_back(1.0); + param_type() { + _Init_base(); + _Pvec.push_back(1.0); + _Bvec = {0, 1}; } template - param_type(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2) : _Mypbase(_Noinit), _Bvec(_First1, _Last1) { + param_type(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2) : _Bvec(_First1, _Last1) { if (2 <= _Bvec.size()) { for (size_t _Idx = 0; _Idx < _Bvec.size(); ++_Idx) { - this->_Pvec.push_back(static_cast(*_First2++)); + _Pvec.push_back(static_cast(*_First2++)); } } else { // default construct _Bvec = {0, 1}; @@ -4443,12 +4435,12 @@ public: } template - param_type(initializer_list<_Ty> _Ilist, _Fn _Func) : _Mypbase(_Noinit) { + param_type(initializer_list<_Ty> _Ilist, _Fn _Func) { if (2 <= _Ilist.size()) { _Bvec.assign(_Ilist); for (const auto& _Bval : _Bvec) { - this->_Pvec.push_back(_Func(_Bval)); + _Pvec.push_back(_Func(_Bval)); } } else { // default construct _Bvec = {0, 1}; @@ -4458,7 +4450,7 @@ public: } template - param_type(size_t _Count, _Ty _Low, _Ty _High, _Fn _Func) : _Mypbase(_Noinit) { + param_type(size_t _Count, _Ty _Low, _Ty _High, _Fn _Func) { _Ty _Range = _High - _Low; _STL_ASSERT(_Ty{0} < _Range, "invalid range for piecewise_linear_distribution"); if (_Count < 1) { @@ -4469,14 +4461,13 @@ public: for (size_t _Idx = 0; _Idx <= _Count; ++_Idx) { // compute _Bvec and _Pvec _Ty _Bval = _Low + _Idx * _Range; _Bvec.push_back(_Bval); - this->_Pvec.push_back(_Func(_Bval)); + _Pvec.push_back(_Func(_Bval)); } _Init(); } _NODISCARD friend bool operator==(const param_type& _Left, const param_type& _Right) { - return static_cast(_Left) == static_cast(_Right) - && _Left._Bvec == _Right._Bvec; + return _Left._Pvec == _Right._Pvec && _Left._Bvec == _Right._Bvec; } #if !_HAS_CXX20 @@ -4492,47 +4483,101 @@ public: #pragma warning(push) #pragma warning(disable : 4244) // '%s': conversion from '%s' to '%s', possible loss of data _NODISCARD vector<_Ty> densities() const { - vector<_Ty> _Ans(this->_Pvec.begin(), this->_Pvec.end()); + vector<_Ty> _Ans(_Pvec.begin(), _Pvec.end()); return _Ans; } #pragma warning(pop) _NODISCARD double _Piece_probability(const size_t _Idx) const { - return 0.5 * (this->_Pvec[_Idx] + this->_Pvec[_Idx + 1]) - * static_cast(_Bvec[_Idx + 1] - _Bvec[_Idx]); + return 0.5 * (_Pvec[_Idx] + _Pvec[_Idx + 1]) * static_cast(_Bvec[_Idx + 1] - _Bvec[_Idx]); + } + + void _Init_base(const bool _Renorm = true) { // initialize like discrete_distribution::param_type + const size_t _Size = _Pvec.size(); + + if (_Renorm) { + if (_Pvec.empty()) { + _Pvec.push_back(1.0); // make empty vector degenerate + } else { // normalize probabilities + double _Sum = 0; + + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { // sum all probabilities + _STL_ASSERT(0.0 <= _Pvec[_Idx], "invalid probability for piecewise_linear_distribution"); + _Sum += _Pvec[_Idx]; + } + + _STL_ASSERT(0.0 < _Sum, "invalid probability vector for piecewise_linear_distribution"); + if (_Sum != 1.0) { + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { + _Pvec[_Idx] /= _Sum; + } + } + } + } + + _Pcdf.assign(1, _Pvec[0]); + for (size_t _Idx = 1; _Idx < _Size; ++_Idx) { + _Pcdf.push_back(_Pvec[_Idx] + _Pcdf[_Idx - 1]); + } } - void _Init(bool _Renorm = true) { // initialize - size_t _Size = this->_Pvec.size(); - size_t _Idx; + void _Init(const bool _Renorm = true) { // initialize + const size_t _Size = _Pvec.size(); if (_Renorm) { - if (this->_Pvec.empty()) { // make empty vector degenerate - this->_Pvec = {1.0, 1.0}; + if (_Pvec.empty()) { // make empty vector degenerate + _Pvec = {1.0, 1.0}; } else { // normalize probabilities double _Sum = 0; - _STL_ASSERT(0.0 <= this->_Pvec[0], "invalid probability for piecewise_linear_distribution"); - for (_Idx = 1; _Idx < _Size; ++_Idx) { // sum all probabilities - _STL_ASSERT(0.0 <= this->_Pvec[_Idx], "invalid probability for piecewise_linear_distribution"); + _STL_ASSERT(0.0 <= _Pvec[0], "invalid probability for piecewise_linear_distribution"); + for (size_t _Idx = 1; _Idx < _Size; ++_Idx) { // sum all probabilities + _STL_ASSERT(0.0 <= _Pvec[_Idx], "invalid probability for piecewise_linear_distribution"); _Sum += _Piece_probability(_Idx - 1); } _STL_ASSERT(0.0 < _Sum, "invalid probability vector for piecewise_linear_distribution"); if (_Sum != 1.0) { - for (_Idx = 0; _Idx < _Size; ++_Idx) { - this->_Pvec[_Idx] /= _Sum; + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { + _Pvec[_Idx] /= _Sum; } } } } - this->_Pcdf.assign(1, _Piece_probability(0)); - for (_Idx = 2; _Idx < _Size; ++_Idx) { - this->_Pcdf.push_back(_Piece_probability(_Idx - 1) + this->_Pcdf[_Idx - 2]); + _Pcdf.assign(1, _Piece_probability(0)); + for (size_t _Idx = 2; _Idx < _Size; ++_Idx) { + _Pcdf.push_back(_Piece_probability(_Idx - 1) + _Pcdf[_Idx - 2]); + } + } + + template + basic_istream<_Elem, _Traits>& _Read(basic_istream<_Elem, _Traits>& _Istr) { // read state from _Istr + size_t _Nvals; + _Istr >> _Nvals; + _Pvec.clear(); + for (; 0 < _Nvals; --_Nvals) { // get a value and add to vector + double _Val; + _In(_Istr, _Val); + _Pvec.push_back(_Val); + } + _Init_base(false); // don't renormalize, just compute CDF + return _Istr; + } + + template + basic_ostream<_Elem, _Traits>& _Write(basic_ostream<_Elem, _Traits>& _Ostr) const { // write state to _Ostr + _Ostr << ' ' << _Pvec.size(); + + for (const auto& _Val : _Pvec) { + _Out(_Ostr, _Val); } + + return _Ostr; } + vector _Pvec; + vector _Pcdf; vector<_Ty> _Bvec; }; @@ -4600,10 +4645,11 @@ public: template friend basic_istream<_Elem, _Traits>& operator>>(basic_istream<_Elem, _Traits>& _Istr, piecewise_linear_distribution& _Dist) { // read state from _Istr - static_cast(_Dist._Par)._Read(_Istr); + _Dist._Par._Read(_Istr); _Dist._Par._Bvec.clear(); - for (size_t _Idx = _Dist._Par._Pvec.size(); 0 < _Idx; --_Idx) { // get a value and add to intervals vector + for (size_t _Idx = _Dist._Par._Pvec.size(); 0 < _Idx; --_Idx) { + // get a value and add to intervals vector double _Val; _In(_Istr, _Val); _Dist._Par._Bvec.push_back(_Val); @@ -4615,7 +4661,7 @@ public: template friend basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, const piecewise_linear_distribution& _Dist) { // write state to _Ostr - static_cast(_Dist._Par)._Write(_Ostr); + _Dist._Par._Write(_Ostr); for (const auto& _Val : _Dist._Par._Bvec) { _Out(_Ostr, _Val); @@ -4626,7 +4672,7 @@ public: template result_type _Eval(_Engine& _Eng, const param_type& _Par0) _DISTRIBUTION_CONST { - size_t _Px = _Mybase::operator()(_Eng, _Par0); + size_t _Px = discrete_distribution::_Invoke_param_pcdf(_Eng, _Par0._Pcdf); double _Px0 = _Par0._Pvec[_Px]; double _Px1 = _Par0._Pvec[_Px + 1]; uniform_real_distribution<_Ty> _Dist; @@ -4640,6 +4686,7 @@ public: return _Par0._Bvec[_Px] + _Xx0 * (_Par0._Bvec[_Px + 1] - _Par0._Bvec[_Px]); } + discrete_distribution _Unused; // TRANSITION, ABI: this was a base class subobject param_type _Par; }; diff --git a/tests/std/test.lst b/tests/std/test.lst index 5bb68adcb1d..85d7fde5fe5 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -202,6 +202,7 @@ tests\GH_001411_core_headers tests\GH_001530_binomial_accuracy tests\GH_001541_case_sensitive_boolalpha tests\GH_001596_adl_proof_algorithms +tests\GH_001600_random_inheritance tests\GH_001638_dllexport_derived_classes tests\GH_001850_clog_tied_to_cout tests\GH_001858_iostream_exception diff --git a/tests/std/tests/GH_001600_random_inheritance/env.lst b/tests/std/tests/GH_001600_random_inheritance/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_001600_random_inheritance/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp new file mode 100644 index 00000000000..977fa0c19b0 --- /dev/null +++ b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +using namespace std; + +// GH-1600 ": piecewise_linear_distribution::param_type should not inherit from +// discrete_distribution::param_type" +// Note that inheritance between distribution types should also be removed. +template +void test_gh_1600_inheritance() { + using Con = piecewise_constant_distribution; + using Lin = piecewise_linear_distribution; + using Dis = discrete_distribution; + + using ConParam = typename Con::param_type; + using LinParam = typename Lin::param_type; + using DisParam = Dis::param_type; + + STATIC_ASSERT(!is_base_of_v); + STATIC_ASSERT(!is_base_of_v); + + STATIC_ASSERT(!is_base_of_v); + STATIC_ASSERT(!is_base_of_v); +} + +// Until 2026-01, piecewise_constant_distribution and piecewise_linear_distribution +// were both derived from discrete_distribution. Same for their param_type structs. +// Now the discrete_distribution objects are changed from base class subobjects to leading member subobjects. +// Same for piecewise_constant_distribution::param_type. +// For piecewise_linear_distribution::param_type, the original discrete_distribution::param_type subobject is +// decomposed into vectors to avoid imposing invariants. +// In vNext, we should probably remove unused members, so the sizes will be reduced. +template +void test_gh_1600_abi() { + // piecewise_constant_distribution, piecewise_linear_distribution, and their param_type structs + // don't introduce padding bytes under MSVC ABI. So it's OK to just add the sizes. + + // The sizes will probably be reduced in vNext. + + using Con = piecewise_constant_distribution; + using Lin = piecewise_linear_distribution; + using Dis = discrete_distribution; + + using ConParam = typename Con::param_type; + using LinParam = typename Lin::param_type; + using DisParam = Dis::param_type; + + STATIC_ASSERT(sizeof(Con) == sizeof(Dis) + sizeof(ConParam)); + STATIC_ASSERT(sizeof(ConParam) == sizeof(DisParam) + sizeof(vector)); + + STATIC_ASSERT(sizeof(Lin) == sizeof(Dis) + sizeof(LinParam)); + STATIC_ASSERT(sizeof(LinParam) == sizeof(DisParam) + sizeof(vector)); + + // The alignments are likely to be unchanged in vNext. + + STATIC_ASSERT(alignof(Con) == alignof(void*)); + STATIC_ASSERT(alignof(ConParam) == alignof(void*)); + + STATIC_ASSERT(alignof(Lin) == alignof(void*)); + STATIC_ASSERT(alignof(LinParam) == alignof(void*)); +} + +void test() { + test_gh_1600_inheritance(); + test_gh_1600_inheritance(); + test_gh_1600_inheritance(); + + test_gh_1600_abi(); + test_gh_1600_abi(); + test_gh_1600_abi(); +}