From 300da36a5fa2e77ec6b186163e0e5a41475fbc75 Mon Sep 17 00:00:00 2001 From: Treston Cuzzort Date: Mon, 26 Oct 2026 00:53:51 -0500 Subject: [PATCH] Support wide character streams in rational output Use the destination stream's character type and traits for the output operator and its temporary buffer. Wide streams otherwise select the Boolean conversion and print 1 for a nonzero rational. Extend the existing output tests with wide fractions and formatting checks, and update the documented operator signature. Assisted-by: OpenAI Codex --- include/boost/rational.hpp | 11 ++++++----- rational.html | 3 ++- test/rational_test.cpp | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/include/boost/rational.hpp b/include/boost/rational.hpp index 89d2fb44..38a4bd3b 100644 --- a/include/boost/rational.hpp +++ b/include/boost/rational.hpp @@ -965,11 +965,12 @@ std::istream& operator>> (std::istream& is, rational& r) } // Add manipulators for output format? -template -std::ostream& operator<< (std::ostream& os, const rational& r) +template +std::basic_ostream& operator<< (std::basic_ostream& os, + const rational& r) { // The slash directly precedes the denominator, which has no prefixes. - std::ostringstream ss; + std::basic_ostringstream ss; ss.copyfmt( os ); ss.tie( NULL ); @@ -978,12 +979,12 @@ std::ostream& operator<< (std::ostream& os, const rational& r) ss << std::noshowpos << std::noshowbase << '/' << r.denominator(); // The numerator holds the showpos, internal, and showbase flags. - std::string const tail = ss.str(); + std::basic_string const tail = ss.str(); std::streamsize const w = os.width() - static_cast( tail.size() ); ss.clear(); - ss.str( "" ); + ss.str( std::basic_string() ); ss.flags( os.flags() ); ss << std::setw( w < 0 || (os.flags() & std::ios::adjustfield) != std::ios::internal ? 0 : w ) << r.numerator(); diff --git a/rational.html b/rational.html index ebacab7f..f25df5fe 100644 --- a/rational.html +++ b/rational.html @@ -121,7 +121,8 @@

Class rational synopsis

// Input and output template <typename I> std::istream& operator>> (std::istream& is, rational<I>& r); -template <typename I> std::ostream& operator<< (std::ostream& os, const rational<I>& r); +template <typename I, typename Ch, typename Tr> +std::basic_ostream<Ch, Tr>& operator<< (std::basic_ostream<Ch, Tr>& os, const rational<I>& r); // Type conversion template <typename T, typename I> T rational_cast (const rational<I>& r); // constexpr since C++11 diff --git a/test/rational_test.cpp b/test/rational_test.cpp index ccd58ea5..46dd887a 100644 --- a/test/rational_test.cpp +++ b/test/rational_test.cpp @@ -1176,6 +1176,43 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( rational_output_test, T, all_signed_test_types ) BOOST_CHECK_EQUAL( oss.str(), "0x22/3db" ); // not "0x22/0x3db" } +#if !defined(BOOST_NO_STD_WSTRING) && !defined(BOOST_NO_STD_WSTREAMBUF) +BOOST_AUTO_TEST_CASE_TEMPLATE( rational_wide_output_test, T, + builtin_signed_test_types ) +{ + using namespace std; + typedef boost::rational rational_type; + + wostringstream oss; + + oss << rational_type( 44, 14 ); + BOOST_CHECK( oss.str() == L"22/7" ); + + // Rational output must not fall back to Boolean conversion. + oss.clear(); oss.str( L"" ); + oss << boolalpha << rational_type( 0 ) << L' ' << rational_type( 1, 3 ); + BOOST_CHECK( oss.str() == L"0/1 1/3" ); + + oss.clear(); oss.str( L"" ); + oss << setw( 5 ) << setfill(L'*') << rational_type( 1, 2 ) << L'r'; + BOOST_CHECK( oss.str() == L"**1/2r" ); + BOOST_CHECK_EQUAL( oss.width(), 0 ); + BOOST_CHECK_EQUAL( oss.fill(), L'*' ); + + oss.clear(); oss.str( L"" ); + oss << showpos << rational_type( 2, 3 ) << noshowpos; + BOOST_CHECK( oss.str() == L"+2/3" ); + + oss.clear(); oss.str( L"" ); + oss << setw( 8 ) << internal << rational_type( 36, -15 ) << right << L'r'; + BOOST_CHECK( oss.str() == L"-***12/5r" ); + + oss.clear(); oss.str( L"" ); + oss << showbase << hex << rational_type( 34, 987 ) << noshowbase << dec; + BOOST_CHECK( oss.str() == L"0x22/3db" ); +} +#endif + // Input test, failing BOOST_AUTO_TEST_CASE_TEMPLATE( rational_input_failing_test, T, all_signed_test_types )