/************************************************************************ * * istream_sentry.cpp - test exercising basic_istream::sentry * * $Id: 27.istream.sentry.cpp 503938 2007-02-06 00:41:34Z sebor $ * ************************************************************************ * * 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 #include // for ctype #include // for streambuf #include // for UserChar, UserTraits #include // for rw_printf() #include /************************************************************************ 27.6.1.1.2 Class basic_istream::sentry [lib.istream::sentry] namespace std { template > class basic_istream::sentry { typedef traits traits_type; bool ok_; // exposition only public: explicit sentry (basic_istream& is, bool noskipws = false); ~sentry (); operator bool() const { return ok_; } private: sentry (const sentry&); // not defined sentry& operator= (const sentry&); // not defined }; } -1- The class sentry defines a class that is responsible for doing exception safe prefix and suffix operations. explicit sentry(basic_istream& is, bool noskipws = false); -2- Effects: If is.good() is true, prepares for formatted or unformatted input. First, if is.tie() is not a null pointer, the function calls is.tie()­>flush() to synchronize the output sequence with any associated external C stream. Except that this call can be suppressed if the put area of is.tie() is empty. Further an implementation is allowed to defer the call to flush until a call of is->rdbuf()­>underflow occurs. If no such call occurs before the sentry object is destroyed, the call to flush may be eliminated entirely(279). If noskipws is zero and is.flags() & ios_base::skipws is nonzero, the function extracts and discards each character as long as the next available input character c is a whitespace character. -3- Notes: The constructor explicit sentry(basic_istream& is, bool noskipws = false) uses the currently imbued locale in is, to determine whether the next input character is whitespace or not. -4- To decide if the character cis a whitespace character, the constructor performs "as if" it executes the following code fragment: const ctype& ctype = use_facet >(is.getloc()); if (ctype.is (ctype.space,c) != 0) // c is a whitespace character. -5- If, after any preparation is completed, is.good() is true, ok_ != false otherwise, ok_ == false. During preparation, the constructor may call setstate(failbit) (which may throw ios_base::failure (27.4.4.3))(280) __________________ 279) This will be possible only in functions that are part of the library. The semantics of the constructor used in user code is as specified. 280) The sentry constructor and destructor can also perform additional implementation-dependent operations. -6- [Example: A typical implementation of the sentry constructor might include code such as: template > basic_istream::sentry( basic_istream& is, bool noskipws = false) { ... int_type c; typedef ctype ctype_type; const ctype_type& ctype = use_facet(is.getloc()); while ((c = is.rdbuf()­>snextc()) != traits::eof()) { if (ctype.is(ctype.space,c)==0) { is.rdbuf()­>sputbackc (c); break; } } ... } --end example] ~sentry(); -7- Effects: None. operator bool() const; -8- Effects: Returns ok_. ************************************************************************/ void memfun_info (int line, const char *cname, const char *tname, const char *fname) { // format the ISTREAM and SENTRY environment variables w/o writing // out any output rw_fprintf (0, "%{$ISTREAM!:@}", "%{?}istream%{:}%{?}wistream" "%{:}basic_istream<%s, %s>%{;}%{;}", 'c' == *cname && 'c' == *tname, 'w' == *cname && 'c' == *tname, cname, tname); rw_fprintf (0, "%{$SENTRY!:@}", "%{$ISTREAM}::sentry"); // pass fname through the "%{@}" directive to expand any embedded // %{$XYZ} directives rw_info (0, 0, line, "std::%{$SENTRY}::%{@}", fname); } /***********************************************************************/ template struct Ctype: std::ctype { typedef std::ctype Base; const charT ws_; // whitespace character std::ctype_base::mask table_ [256]; Ctype (unsigned ref, charT white) : Base (ref), ws_ (white) { } // virtuals overridden below used by ctype // ctype uses the table() member instead /* 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); } /* virtual */ const charT* do_is (const charT *from, const charT *to, std::ctype_base::mask *m) const { return Base::is (from, to, m); } /* virtual */ const charT* do_scan_is (std::ctype_base::mask m, const charT *from, const charT *to) const { for (; from != to && !do_is (m, *from); ++from); return from; } /* virtual */ const charT* do_scan_not (std::ctype_base::mask m, const charT *from, const charT *to) const { for (; from != to && do_is (m, *from); ++from); return from; } }; // specialized for ctype _RWSTD_SPECIALIZED_CLASS Ctype::Ctype (unsigned ref, char white) : Base (table_, false, ref), ws_ (white) { for (unsigned 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; } template struct Streambuf: std::basic_streambuf { typedef std::basic_streambuf Base; int nsyncs_; Streambuf (const charT *gbeg, const charT *gend) : Base (), nsyncs_ (0) { this->setg (_RWSTD_CONST_CAST (charT*, gbeg), _RWSTD_CONST_CAST (charT*, gbeg), _RWSTD_CONST_CAST (charT*, gend)); } 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 (); } }; template void test_ctor (const charT*, const Traits*, const char *cname, const char *tname) { typedef std::basic_istream Istream; typedef typename Istream::sentry Sentry; memfun_info (__LINE__, cname, tname, "sentry (%{$ISTREAM}&, bool)"); const charT cbuf[] = { 'a', 'b', 'c', 'd', 'e', ' ', 'f', '\0' }; 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 }; ////////////////////////////////////////////////////////////// // exercise 27.6.1.1.2, p1: // - is.good() is true // - is.tie() is not null // = the function calls is.tie().flush() unsigned iter = 0; // iteration counter for (unsigned i = 0; i != sizeof states / sizeof *states; ++i) { for (unsigned j = 0; j != 2; ++j /* noskipws */) { Streambuf sb (cbuf, cbuf + sizeof cbuf / sizeof *cbuf); Istream is (&sb); // flush() is called iff // all of the following conditions hold const bool flush_called = is.good () && 0 != is.tie (); const Sentry guard (is, 0 != j); _RWSTD_UNUSED (guard); rw_assert (flush_called == sb.nsyncs_, 0, __LINE__, "%u. basic_istream<%s, %s>::sentry::sentry" "(basic_istream &is, bool noskipws = %d); " "expected to call is.flush () %d times, got %d" "initial is.state () = %{Is}, is.flags() & " "ios::skipws = %d", iter, cname, tname, 0 != j, flush_called, sb.nsyncs_, states [i], is.flags () & std::ios_base::skipws); ++iter; } } ////////////////////////////////////////////////////////////// // exercise 27.6.1.1.2, p1: // - is.good() is true // - noskipws is zero // - is.flags() & ios_base::skipws // = the function extracts and discards each character as long // as the next available input character c is a whitespace // character. for (unsigned i = 0; i != sizeof states / sizeof *states; ++i) { for (unsigned j = 0; j != 2; ++j /* noskipws */) { for (unsigned k = 0; k != 2; ++k /* ios_base::skipws */) { for (charT wc = charT ('a'); wc != charT ('c'); ++wc) { const Ctype ctp (1, wc); Streambuf sb (cbuf, cbuf + sizeof cbuf / sizeof *cbuf); Istream is (&sb); is.setstate (states [i]); if (k) is.setf (std::ios_base::skipws); else is.unsetf (std::ios_base::skipws); const std::locale loc = is.imbue (std::locale (is.getloc (), &ctp)); // imbue the previous locale into the stream // buffer to verify that the sentry ctor uses // the locale imbued in the stream object and // not the one in the stream buffer sb.pubimbue (loc); // a whitespace character is extracted iff // all of the following conditions hold const bool extract = is.good () && 0 == j && is.flags () & std::ios_base::skipws && cbuf [0] == wc; const Sentry guard (is, 0 != j); _RWSTD_UNUSED (guard); rw_assert (cbuf + extract == sb.pubgptr (), 0, __LINE__, "%u. %{$SENTRY}::sentry" "(%{$ISTREAM} &is, bool noskipws " "= %b); expected to extract %d " "whitespace chars ('%c') from %{*Ac}, " "extracted %u; initial is.state () = " "%{Is}, is.flags() & ios::skipws = %d", iter, j, extract + 0, char (wc), int (sizeof (*cbuf)), cbuf, sb.pubgptr () - sb.pubeback (), states [i], k); // verify that the ctor doesn't affect gcount() rw_assert (0 == is.gcount (), 0, __LINE__, "%u. %{$SENTRY}::sentry" "(%{$ISTREAM} &is = %{*Ac}, bool noskipws " "= %b); changed is.gcount() from 0 to %i", iter, int (sizeof (*cbuf)), cbuf, j, is.gcount ()); ++iter; } } } } } /***********************************************************************/ template void test_ok (const charT*, const Traits*, const char *cname, const char *tname) { typedef std::basic_istream Istream; typedef typename Istream::sentry Sentry; memfun_info (__LINE__, cname, tname, "operator bool () const"); const charT cbuf[] = { 'a', 'b', 'c', 'd', 'e', ' ', 'f', '\0' }; 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 }; ////////////////////////////////////////////////////////////// // exercise 27.6.1.1.2, p5: // - is.good() is true // - noskipws is zero // - is.flags() & ios_base::skipws // - the function extracts and discards each character as long // as the next available input character c is a whitespace // character // = if, after any preparation is completed, is.good() is true, // ok_ != false otherwise, ok_ == false. unsigned iter = 0; // iteration counter for (unsigned i = 0; i != sizeof states / sizeof *states; ++i) { for (unsigned j = 0; j != 2; ++j /* noskipws */) { for (unsigned k = 0; k != 2; ++k /* ios_base::skipws */) { for (charT wc = charT ('a'); wc != charT ('c'); ++wc) { const Ctype ctp (1, wc); Streambuf sb (cbuf, cbuf + sizeof cbuf / sizeof *cbuf); Istream is (&sb); is.setstate (states [i]); if (k) is.setf (std::ios_base::skipws); else is.unsetf (std::ios_base::skipws); const std::locale loc = is.imbue (std::locale (is.getloc (), &ctp)); // imbue the previous locale into the stream // buffer to verify that the sentry ctor uses // the locale imbued in the stream object and // not the one in the stream buffer sb.pubimbue (loc); const Sentry guard (is, 0 != j); _RWSTD_UNUSED (guard); const bool success = is.good () && guard || !is.good () && !guard; rw_assert (success, 0, __LINE__, "%u. %{$SENTRY}" "(%{$ISTREAM} &is, bool noskipws " "= %d).operator bool() == %d; initial " "is.state() = %{Is}, is.flags() & " "ios::skipws = %d", iter, j, is.good (), states [i], k); ++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(what, charT, Traits) \ test_ ## what ((charT*)0, (Traits*)0, #charT, #Traits) using namespace std; if (rw_note (0 <= opt_char && 0 <= opt_char_traits, 0, __LINE__, "istream::sentry tests disabled")) { TEST (ctor, char, char_traits); TEST (ok, char, char_traits); } if (rw_note (0 <= opt_char && 0 <= opt_user_traits, 0, __LINE__, "basic_istream::sentry " "tests disabled")) { TEST (ctor, char, UserTraits); TEST (ok, char, UserTraits); } #ifndef _RWSTD_NO_WCHAR_T if (rw_note (0 <= opt_wchar && 0 <= opt_char_traits, 0, __LINE__, "wistream::sentry tests disabled")) { TEST (ctor, wchar_t, char_traits); TEST (ok, wchar_t, char_traits); } if (rw_note (0 <= opt_wchar && 0 <= opt_user_traits, 0, __LINE__, "basic_istream::sentry " "tests disabled")) { TEST (ctor, wchar_t, UserTraits); TEST (ok, wchar_t, UserTraits); } #endif // _RWSTD_NO_WCHAR_T return 0; } /***********************************************************************/ int main (int argc, char *argv[]) { return rw_test (argc, argv, __FILE__, "istream.sentry", 0 /* no comment */, run_test, "|-char~ " "|-wchar_t~ " "|-char_traits~ " "|-UserTraits~ ", &opt_char, &opt_wchar, &opt_char_traits, &opt_user_traits, (void*)0 /* sentinel */); }