From eb2ee46814391a0a037b04c0178fe7dc8bb6517e Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Tue, 3 Oct 2023 21:09:01 +0800 Subject: [PATCH 1/2] Implement LWG-3561 Issue with internal counter in discard_block_engine Also correct the types of static data members `block_size` and `used_block`. --- stl/inc/random | 90 ++++++++++++++++++- tests/std/test.lst | 1 + .../env.lst | 4 + .../test.cpp | 82 +++++++++++++++++ tests/tr1/tests/random4/test.cpp | 2 +- 5 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 tests/std/tests/LWG3561_discard_block_engine_counter/env.lst create mode 100644 tests/std/tests/LWG3561_discard_block_engine_counter/test.cpp diff --git a/stl/inc/random b/stl/inc/random index ddf08e72236..377d5cb8095 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -1437,14 +1437,100 @@ private: int _Nx; }; +template +class _Discard_block_base { // TRANSITION, ABI, should be merged into discard_block_engine +public: + using base_type = _Engine; + using result_type = typename _Engine::result_type; + + _Discard_block_base() : _Eng(), _Nx(0) {} + + explicit _Discard_block_base(const _Engine& _Ex) : _Eng(_Ex), _Nx(0) {} + + explicit _Discard_block_base(result_type _Seed) : _Eng(_Seed), _Nx(0) {} + + template = 0> + explicit _Discard_block_base(_Seed_seq& _Seq) : _Eng(_Seq), _Nx(0) {} + + void seed() { // seed engine from default value + _Eng.seed(); + _Nx = 0; + } + + void seed(result_type _Xx0) { // seed engine from specified value + _Eng.seed(_Xx0); + _Nx = 0; + } + + template = 0> + void seed(_Seed_seq& _Seq) { // seed engine from seed sequence + _Eng.seed(_Seq); + _Nx = 0; + } + + _NODISCARD const base_type& base() const noexcept { + return _Eng; + } + + _NODISCARD result_type operator()() { + if (_Rx <= _Nx) { // discard values + while (_Nx++ < _Px) { + (void) _Eng(); + } + + _Nx = 0; + } + ++_Nx; + return _Eng(); + } + + void discard(unsigned long long _Nskip) { // discard _Nskip elements + for (; 0 < _Nskip; --_Nskip) { + (void) (*this)(); + } + } + + _NODISCARD_FRIEND bool operator==(const _Discard_block_base& _Left, const _Discard_block_base& _Right) { + return _Left._Eng == _Right._Eng && _Left._Nx == _Right._Nx; + } + +#if !_HAS_CXX20 + _NODISCARD_FRIEND bool operator!=(const _Discard_block_base& _Left, const _Discard_block_base& _Right) { + return !(_Left == _Right); + } +#endif // !_HAS_CXX20 + + template + friend basic_istream<_Elem, _Traits>& operator>>( + basic_istream<_Elem, _Traits>& _Istr, _Discard_block_base& _Eng) { // read state from _Istr + return _Istr >> _Eng._Eng >> _Eng._Nx; + } + + template + friend basic_ostream<_Elem, _Traits>& operator<<( + basic_ostream<_Elem, _Traits>& _Ostr, const _Discard_block_base& _Eng) { // write state to _Ostr + return _Ostr << _Eng._Eng << ' ' << _Eng._Nx; + } + +private: + base_type _Eng; + size_t _Nx; +}; + _EXPORT_STD template -class discard_block_engine : public discard_block<_Engine, _Px, _Rx> { // discard_block_engine compound engine +class discard_block_engine // discard_block_engine compound engine + : public conditional_t<_Px <= INT_MAX, discard_block<_Engine, static_cast(_Px), static_cast(_Rx)>, + _Discard_block_base<_Engine, _Px, _Rx>> { public: static_assert(0 < _Rx && _Rx <= _Px, "invalid template argument for discard_block_engine"); - using _Mybase = discard_block<_Engine, _Px, _Rx>; + using _Mybase = conditional_t<_Px <= INT_MAX, discard_block<_Engine, static_cast(_Px), static_cast(_Rx)>, + _Discard_block_base<_Engine, _Px, _Rx>>; using result_type = typename _Engine::result_type; + static constexpr size_t block_size = _Px; + static constexpr size_t used_block = _Rx; + discard_block_engine() : _Mybase() {} explicit discard_block_engine(const _Engine& _Ex) : _Mybase(_Ex) {} diff --git a/tests/std/test.lst b/tests/std/test.lst index 8c1a3602607..e7f9259ad96 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -242,6 +242,7 @@ tests\LWG3234_math_special_overloads tests\LWG3422_seed_seq_ctors tests\LWG3480_directory_iterator_range tests\LWG3545_pointer_traits_sfinae +tests\LWG3561_discard_block_engine_counter tests\LWG3610_iota_view_size_and_integer_class tests\P0009R18_mdspan_default_accessor tests\P0009R18_mdspan_extents diff --git a/tests/std/tests/LWG3561_discard_block_engine_counter/env.lst b/tests/std/tests/LWG3561_discard_block_engine_counter/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/LWG3561_discard_block_engine_counter/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/LWG3561_discard_block_engine_counter/test.cpp b/tests/std/tests/LWG3561_discard_block_engine_counter/test.cpp new file mode 100644 index 00000000000..f69c4bc8d0c --- /dev/null +++ b/tests/std/tests/LWG3561_discard_block_engine_counter/test.cpp @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +using namespace std; + +constexpr size_t large_block_size = static_cast(UINT_MAX) - 16; +constexpr size_t large_used_block = static_cast(UINT_MAX) - 20; + +struct trivial_engine { + using result_type = size_t; + + static constexpr size_t min() { + return 0; + } + static constexpr size_t max() { + return SIZE_MAX; + } + + size_t operator()() noexcept { + return counter_++; + } + +#if _HAS_CXX20 + friend bool operator==(const trivial_engine&, const trivial_engine&) = default; +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + friend bool operator==(const trivial_engine& lhs, const trivial_engine& rhs) noexcept { + return lhs.counter_ == rhs.counter_; + } + friend bool operator!=(const trivial_engine& lhs, const trivial_engine& rhs) noexcept { + return lhs.counter_ != rhs.counter_; + } +#endif // ^^^ !_HAS_CXX20 ^^^ + + template + friend basic_istream& operator>>(basic_istream& is, trivial_engine& eng) { + return is >> eng.counter_; + } + + template + friend basic_ostream& operator<<(basic_ostream& os, const trivial_engine& eng) { + return os << eng.counter_; + } + + size_t counter_ = 0; +}; + +using trivial_discard_block = discard_block_engine; + +void test_lwg_3561() { + trivial_discard_block e{}; + e.discard(168); + auto rep = (ostringstream{} << e).str(); + + assert(e == e); + assert(!(e != e)); + assert(rep == "168 168"); // relies on the implementation-specific details of operator<< +} + +// Also tests the type correctness of discard_block_engine::block_size/used_block + +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); + +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); + +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); + +int main() { + test_lwg_3561(); +} diff --git a/tests/tr1/tests/random4/test.cpp b/tests/tr1/tests/random4/test.cpp index 0443f3c6ddf..b9ad14f27e8 100644 --- a/tests/tr1/tests/random4/test.cpp +++ b/tests/tr1/tests/random4/test.cpp @@ -231,7 +231,7 @@ static void tsubtract() { } static void tdiscard() { - int i; + STD size_t i; typedef STD subtract_with_carry_engine rng_base_t; typedef STD discard_block_engine rng_t; CHECK_INT(rng_t::block_size, 223); From dc6313e35b924793f14c49ca956b39c17a8ea130 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 3 Oct 2023 16:33:22 -0700 Subject: [PATCH 2/2] Code review feedback. --- .../std/tests/LWG3561_discard_block_engine_counter/test.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/LWG3561_discard_block_engine_counter/test.cpp b/tests/std/tests/LWG3561_discard_block_engine_counter/test.cpp index f69c4bc8d0c..9e98e4a971c 100644 --- a/tests/std/tests/LWG3561_discard_block_engine_counter/test.cpp +++ b/tests/std/tests/LWG3561_discard_block_engine_counter/test.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include +#include #include #include #include @@ -13,8 +14,8 @@ using namespace std; -constexpr size_t large_block_size = static_cast(UINT_MAX) - 16; -constexpr size_t large_used_block = static_cast(UINT_MAX) - 20; +constexpr size_t large_block_size = UINT_MAX - 16; +constexpr size_t large_used_block = UINT_MAX - 20; struct trivial_engine { using result_type = size_t;