| /************************************************************************ |
| * |
| * 27.istream.manip.cpp - test exercising the basic_istream ws manipulator |
| * |
| * $Id$ |
| * |
| ************************************************************************ |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed |
| * with this work for additional information regarding copyright |
| * ownership. The ASF licenses this file to you under the Apache |
| * License, Version 2.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * permissions and limitations under the License. |
| * |
| * Copyright 2003-2006 Rogue Wave Software. |
| * |
| **************************************************************************/ |
| |
| #include <cstddef> |
| #include <istream> |
| |
| #include <rw_driver.h> |
| |
| /************************************************************************ |
| |
| namespace std { |
| template <class charT, class traits> |
| basic_istream<charT,traits>& ws(basic_istream<charT,traits>& is); |
| } |
| |
| -1- Effects: Extracts characters as long as the next available |
| character c is whitespace or until there are no more characters |
| in the sequence. Whitespace characters are distinguished with |
| the same criterion as used by sentry::sentry (27.6.1.1.2). If |
| ws stops extracting characters because there are no more |
| available it sets eofbit, but not failbit. |
| -2- Returns: is |
| |
| ************************************************************************/ |
| |
| template <class charT> |
| struct Ctype: std::ctype<charT> |
| { |
| typedef std::ctype<charT> Base; |
| |
| const charT ws_; // whitespace character |
| |
| std::ctype_base::mask table_ [256]; |
| |
| Ctype (std::size_t ref, charT white) |
| : Base (ref), ws_ (white) { } |
| |
| // used by ctype<wchar_t> |
| /* virtual */ bool |
| do_is (std::ctype_base::mask m, charT c) const { |
| if (m & std::ctype_base::space) |
| return ws_ == c; |
| return Base::is (m, c); |
| } |
| |
| // not used by the test, defined only to silence compiler warnings |
| // about the overload above hiding the base function overloaded below |
| /* virtual */ const charT* |
| do_is (const charT *lo, const charT *hi, std::ctype_base::mask *m) const { |
| return Base::do_is (lo, hi, m); |
| } |
| }; |
| |
| |
| // specialized for ctype<char> |
| _RWSTD_SPECIALIZED_CLASS |
| Ctype<char>::Ctype (std::size_t ref, char white) |
| : Base (table_, false, ref), ws_ (white) |
| { |
| for (std::size_t i = 0; i != sizeof table_ / sizeof *table_; ++i) |
| table_ [i] = std::ctype_base::mask (); |
| |
| typedef unsigned char UChar; |
| |
| table_ [UChar (ws_)] = std::ctype_base::space; |
| } |
| |
| |
| // user-defined Traits type exercises the ability to instantiate |
| // std::ws and any supporting templates on types other than the |
| // default std::char_traits<charT> |
| template <class charT> |
| struct UserTraits: std::char_traits<charT> { }; |
| |
| |
| template <class charT, class Traits> |
| struct Streambuf: std::basic_streambuf<charT, Traits> |
| { |
| typedef std::basic_streambuf<charT, Traits> Base; |
| |
| typedef typename Base::traits_type traits_type; |
| typedef typename Base::int_type int_type; |
| |
| int nsyncs_; // number of calls to sync() |
| |
| // overflow() will return eof () after extracting this many chars |
| int fail_after_; |
| |
| // overflow() will throw am exception after extracting this many chars |
| int throw_after_; |
| |
| const charT *end_; // the end of the buffer (future eback()) |
| |
| Streambuf (const charT *gbeg, const charT *gend) |
| : Base (), |
| nsyncs_ (0), |
| fail_after_ (-1), // never fail |
| throw_after_ (-1), // never throw |
| end_ (gend) { |
| this->setg (_RWSTD_CONST_CAST (charT*, gbeg), |
| _RWSTD_CONST_CAST (charT*, gbeg), |
| _RWSTD_CONST_CAST (charT*, gbeg)); |
| } |
| |
| const charT* pubeback () const { |
| return this->eback (); |
| } |
| |
| const charT* pubgptr () const { |
| return this->gptr (); |
| } |
| |
| const charT* pubegptr () const { |
| return this->egptr (); |
| } |
| |
| /* virtual */ int sync () { |
| ++nsyncs_; |
| return Base::sync (); |
| } |
| |
| /* virtual */ int_type underflow () { |
| if (this->gptr () == end_) |
| return traits_type::eof (); |
| |
| if (throw_after_ == this->gptr () - this->eback ()) { |
| |
| #ifndef _RWSTD_NO_EXCEPTIONS |
| |
| throw throw_after_; |
| |
| #else // if defined (_RWSTD_NO_EXCEPTIONS) |
| |
| return traits_type::eof (); |
| |
| #endif // _RWSTD_NO_EXCEPTIONS |
| |
| } |
| |
| if (fail_after_ == this->gptr () - this->eback ()) |
| return traits_type::eof (); |
| |
| this->setg (this->eback (), this->gptr (), this->gptr () + 1); |
| return traits_type::to_int_type (*this->gptr ()); |
| } |
| }; |
| |
| /***********************************************************************/ |
| |
| template <class charT, class Traits> |
| void |
| test_ws (const Traits* /* dummy */, |
| const char *cname, |
| const char *tname, |
| std::size_t iter, |
| const charT *cbuf, |
| std::size_t cbuf_size, |
| std::ios_base::iostate state, |
| std::size_t j, |
| bool skipws, |
| charT white) |
| { |
| typedef std::basic_istream<charT, Traits> Istream; |
| typedef typename Istream::sentry Sentry; |
| |
| // construct a ctype object that treats `wc' as the only |
| // whitespace character |
| const Ctype<charT> ctp (1, white); |
| |
| // construct a stream buffer object and initialize its read sequence |
| // with the character buffer |
| Streambuf<charT, Traits> sb (cbuf, cbuf + cbuf_size - 1); |
| |
| // set err_after to the number of successful extractions from |
| // the stream; subsequent extraction will cause an error |
| const std::size_t err_after = j % (cbuf_size - 1); |
| const char *err_type; |
| |
| if (j < cbuf_size - 1) { |
| // have the stream buffer object's underflow() fail (by throwing |
| // an exception if possible) after j characters have been extracted |
| // (this object calls underflow() for every char) |
| sb.throw_after_ = int (err_after); |
| err_type = "threw"; |
| } |
| else { |
| // have the stream buffer object's underflow() fail by returning |
| // eof after j characters have been extracted (this object calls |
| // underflow() for every char) |
| sb.fail_after_ = int (err_after); |
| err_type = "returned EOF"; |
| } |
| |
| // construct an istream object and initialize it with the user |
| // defined streambuf object |
| Istream is (&sb); |
| |
| // set the stream object's initial state |
| is.setstate (state); |
| |
| // set or clear the skipws bit |
| if (skipws) |
| is.setf (std::ios_base::skipws); |
| else |
| is.unsetf (std::ios_base::skipws); |
| |
| // construct a locale object containing the user-defined ctype object |
| const std::locale loc = is.imbue (std::locale (is.getloc (), &ctp)); |
| |
| // imbue the previous locale into the stream buffer to verify that |
| // the ws manipulator uses the locale imbued in the stream object |
| // and not the one in the stream buffer |
| sb.pubimbue (loc); |
| |
| // the number of extracted whitespace characters expected to |
| // be reported by gcount() must equal the number of successful |
| // extractions computed by the the stream buffer overridden |
| // underflow member function |
| const int extract = is.good () && white == cbuf [0] ? int (err_after) : 0; |
| |
| #ifndef _RWSTD_NO_EXCEPTIONS |
| |
| bool caught = false; |
| |
| // on every other iteration set badbit in exceptions to check |
| // that the thrown object is propagated by the function |
| if (err_after % 2 && !is.bad ()) |
| is.exceptions (std::ios_base::badbit); |
| |
| try { |
| std::ws (is); |
| } |
| catch (int) { |
| caught = true; |
| } |
| catch (...) { |
| |
| caught = true; |
| |
| rw_assert (false, 0, __LINE__, |
| "%u. std::ws (basic_istream<%s, %s >&);" |
| " threw exception of unexpected type", |
| iter, cname, tname); |
| } |
| |
| ////////////////////////////////////////////////////////////////// |
| // verify that the function propagates exceptions thrown from the |
| // streambuf object only when badbit is set in the stream object's |
| // exceptions() |
| |
| rw_assert (!(caught && !(err_after % 2)), 0, __LINE__, |
| "%u. std::ws (basic_istream<%s, %s>&);" |
| " unexpectedly propagated exception", |
| iter, cname, tname); |
| |
| #else // if defined (_RWSTD_NO_EXCEPTIONS) |
| |
| std::ws (is); |
| |
| #endif // _RWSTD_NO_EXCEPTIONS |
| |
| ////////////////////////////////////////////////////////////////// |
| // verify that the expected number of whitespace characters have |
| // been extracted from the stream |
| |
| rw_assert (cbuf + extract == sb.pubgptr (), 0, __LINE__, |
| "%u. std::ws (basic_istream<%s, %s>&);" |
| " expected to extract %d " |
| "whitespace chars ('%c') from %{*Ac}, " |
| "extracted %u; initial rdstate() = %{Is}, " |
| "is.flags() & ios::skipws = %d, " |
| "underflow %s at extraction %u", |
| iter, cname, tname, extract, (char)(white), |
| int (sizeof *cbuf), cbuf, |
| sb.pubgptr () - sb.pubeback (), state, |
| skipws, err_type, err_after); |
| |
| ////////////////////////////////////////////////////////////////// |
| // verify that gcount() value not affected (27.6.1.4 p1) |
| |
| rw_assert (0 == is.gcount (), 0, __LINE__, |
| "%u. std::ws (basic_istream<%s, %s >&)." |
| "gcount() == %d, got %d; whitespace is" |
| " '%c', input is %{*Ac}, initial rdstate() = %{Is}, " |
| "flags() & ios::skipws = %d," |
| " underflow %s at extraction %u", |
| iter, cname, tname, extract, is.gcount (), |
| (char)(white), int (sizeof *cbuf), cbuf, state, |
| skipws, err_type, err_after); |
| |
| ////////////////////////////////////////////////////////////////// |
| // verify the state of the stream object after the function call |
| |
| // expected stream state after the function call is unchanged |
| // (i.e., the initial stream state), // except... |
| std::ios_base::iostate expect_state = state; |
| |
| if (state) { |
| // lwg 419 |
| expect_state |= std::ios_base::failbit; |
| } |
| else if (white == cbuf [0] || !err_after) { |
| |
| #ifndef _RWSTD_NO_EXCEPTIONS |
| |
| // ...when any whitespace characters are extracted, or the |
| // first character on the stream is peeked at, even if it's |
| // not a space, and an exception is thrown during input, |
| // badbit should be set, otherwise eofbit (bot not failbit) |
| // should be set |
| if (j < cbuf_size - 1) |
| expect_state = std::ios_base::badbit; |
| else |
| expect_state = std::ios_base::eofbit; |
| |
| #else // if defined (_RWSTD_NO_EXCEPTIONS) |
| |
| expect_state = std::ios_base::eofbit; |
| |
| #endif // _RWSTD_NO_EXCEPTIONS |
| } |
| |
| rw_assert (is.rdstate () == expect_state, 0, __LINE__, |
| "%u. std::ws (basic_istream<%s, %s >&)." |
| "rdstate() == %{Is}, got %{Is}; " |
| "extracting whitespace ('%c') from %{*Ac}, " |
| "extracted %u; initial is.rdstate() = %{Is}, " |
| "is.flags() & ios::skipws = %d, underflow %s " |
| "at extraction %u", |
| iter, cname, tname, |
| expect_state, is.rdstate(), |
| (char)(white), int (sizeof *cbuf), cbuf, |
| sb.pubgptr () - sb.pubeback (), state, |
| skipws, err_type, err_after); |
| } |
| |
| /***********************************************************************/ |
| |
| |
| template <class charT, class Traits> |
| void test_ws (charT*, Traits*, const char *cname, const char *tname) |
| { |
| rw_info (0, 0, __LINE__, "std::basic_istream<%s, %s>& " |
| "std::ws (std::basic_istream<%1$s, %2$s>&)", |
| cname, tname); |
| |
| const charT cbuf[] = { 'a', 'a', 'a', '\0' }; |
| const std::size_t cbuf_size = sizeof cbuf / sizeof *cbuf; |
| |
| const std::ios_base::iostate states[] = { |
| std::ios_base::badbit, |
| std::ios_base::eofbit, |
| std::ios_base::failbit, |
| std::ios_base::goodbit, |
| std::ios_base::badbit | std::ios_base::eofbit, |
| std::ios_base::badbit | std::ios_base::failbit, |
| std::ios_base::eofbit | std::ios_base::failbit, |
| std::ios_base::badbit | std::ios_base::eofbit | std::ios_base::failbit |
| }; |
| |
| const std::size_t nstates = sizeof states / sizeof *states; |
| |
| { ////////////////////////////////////////////////////////////// |
| // exercise 27.6.1.4, p1 |
| |
| std::size_t iter = 0; // iteration counter |
| |
| // iterate over all possible settings of the stream state |
| for (std::size_t i = 0; i != nstates; ++i) { |
| |
| // iterate twice over all characters in the character buffer, |
| // once to trigger an exception at each iteration (except |
| // the last one), and then again to have underflow() fail |
| // by returning EOF at each iteration (except the last one) |
| for (std::size_t j = 0; j != 2 * cbuf_size - 1; ++j) { |
| |
| // iterate over the ios_base::skipws setting |
| for (std::size_t k = 0; k != 2; ++k /* skipws */) { |
| |
| // iterate over two whitespace characters |
| for (charT wc = charT ('a'); wc != charT ('c'); ++wc) { |
| |
| test_ws ((Traits*)0, cname, tname, iter, |
| cbuf, cbuf_size, states [i], j, !!k, wc); |
| |
| ++iter; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /***********************************************************************/ |
| |
| static int opt_char; |
| static int opt_wchar; |
| static int opt_char_traits; |
| static int opt_user_traits; |
| |
| |
| static int |
| run_test (int, char**) |
| { |
| #define TEST(charT, Traits) \ |
| test_ws ((charT*)0, (Traits*)0, #charT, #Traits) |
| |
| if (rw_note (0 <= opt_char && 0 <= opt_char_traits, 0, __LINE__, |
| "ws (basic_istream<char, char_traits<char>>) disabled")) |
| TEST (char, std::char_traits<char>); |
| |
| if (rw_note (0 <= opt_char && 0 <= opt_user_traits, 0, __LINE__, |
| "ws (basic_istream<char, UserTraits<char>>) disabled")) |
| TEST (char, UserTraits<char>); |
| |
| #ifndef _RWSTD_NO_WCHAR_T |
| |
| if (rw_note (0 <= opt_wchar && 0 <= opt_char_traits, 0, __LINE__, |
| "ws (basic_istream<wchar_t, char_traits<wchar_t>>) disabled")) |
| TEST (wchar_t, std::char_traits<wchar_t>); |
| |
| if (rw_note (0 <= opt_wchar && 0 <= opt_user_traits, 0, __LINE__, |
| "ws (basic_istream<wchar_t, UserTraits<wchar_t>>) disabled")) |
| TEST (wchar_t, UserTraits<wchar_t>); |
| |
| #endif // _RWSTD_NO_WCHAR_T |
| |
| return 0; |
| } |
| |
| /***********************************************************************/ |
| |
| int main (int argc, char *argv[]) |
| { |
| return rw_test (argc, argv, __FILE__, |
| "istream.manip", |
| 0 /* no comment */, |
| run_test, |
| "|-char~ " |
| "|-wchar_t~ " |
| "|-char_traits~ " |
| "|-UserTraits~ ", |
| &opt_char, |
| &opt_wchar, |
| &opt_char_traits, |
| &opt_user_traits, |
| (void*)0 /* sentinel */); |
| } |