blob: 1125f259b1716200ecf7469911fe14b57d95f4e5 [file] [log] [blame]
/************************************************************************
*
* istream_sentry.cpp - test exercising basic_istream<charT>::sentry
*
* $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 <istream>
#include <locale> // for ctype
#include <streambuf> // for streambuf
#include <rw_char.h> // for UserChar, UserTraits
#include <rw_printf.h> // for rw_printf()
#include <driver.h>
/************************************************************************
27.6.1.1.2 Class basic_istream::sentry [lib.istream::sentry]
namespace std {
template <class charT, class traits = char_traits<charT> >
class basic_istream<charT, traits>::sentry {
typedef traits traits_type;
bool ok_; // exposition only
public:
explicit
sentry (basic_istream<charT, traits>& 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<charT, traits>& 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<charT, traits>&
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<charT>& ctype = use_facet<ctype<charT> >(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 <class charT, class traits = char_traits<charT> >
basic_istream<charT,traits>::sentry(
basic_istream<charT,traits>& is, bool noskipws = false) {
...
int_type c;
typedef ctype<charT> ctype_type;
const ctype_type& ctype = use_facet<ctype_type>(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 <class charT>
struct Ctype: std::ctype<charT>
{
typedef std::ctype<charT> 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<wchar_t>
// ctype<char> 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<char>
_RWSTD_SPECIALIZED_CLASS
Ctype<char>::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 <class charT, class Traits>
struct Streambuf: std::basic_streambuf<charT, Traits>
{
typedef std::basic_streambuf<charT, Traits> 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 <class charT, class Traits>
void test_ctor (const charT*, const Traits*,
const char *cname, const char *tname)
{
typedef std::basic_istream<charT, Traits> 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<charT, Traits>
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<charT> ctp (1, wc);
Streambuf<charT, Traits>
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 <class charT, class Traits>
void test_ok (const charT*, const Traits*,
const char *cname, const char *tname)
{
typedef std::basic_istream<charT, Traits> 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<charT> ctp (1, wc);
Streambuf<charT, Traits>
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<char>);
TEST (ok, char, char_traits<char>);
}
if (rw_note (0 <= opt_char && 0 <= opt_user_traits, 0, __LINE__,
"basic_istream<char, UserTraits<char>::sentry "
"tests disabled")) {
TEST (ctor, char, UserTraits<char>);
TEST (ok, char, UserTraits<char>);
}
#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<wchar_t>);
TEST (ok, wchar_t, char_traits<wchar_t>);
}
if (rw_note (0 <= opt_wchar && 0 <= opt_user_traits, 0, __LINE__,
"basic_istream<wchar_t, UserTraits<wchar_t>::sentry "
"tests disabled")) {
TEST (ctor, wchar_t, UserTraits<wchar_t>);
TEST (ok, 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.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 */);
}