Martin, I implemented your latest notes about testsuite exception
(rw_exception.h, exception.cpp). The updated version is attached.
> -----Original Message-----
> From: Martin Sebor [mailto:[EMAIL PROTECTED]
> Sent: Wednesday, July 12, 2006 4:27 AM
> To: [email protected]
> Subject: Re: design of testuite exceptions (was: Re: svn
> commit: r418319 -
> /incubator/stdcxx/trunk/tests/strings/21.string.io.cpp)
>
> Okay. I think I would still like to remove the logtostderr
> argument and do the logging in new.cpp.
Done
> Also, it would be more efficient to avoid catching and
> rethrowing the exception only to call va_end() on the
> argument list.
Done
> Finally,
[...]
> we want to catch the same type and
> maybe just throw different (but not visible) types derived
> from it.
Done
> Moving on to allocator.cpp
[...]
> the fifth argument to rw_throw: "SharedAlloc::funcall"
> should be the name of the function or 0 (in which case
> rw_throw would need to avoid trying to format it):
Done
> I would be happier with catching the exception here (by
> reference), logging the what() string when logging/tracing is
> enabled, and then rethrowing it, than be stuck with the extra
> argument to rw_throw().
Done
Farid.
Index: allocator.cpp
===================================================================
--- allocator.cpp (revision 421257)
+++ allocator.cpp (working copy)
@@ -34,6 +34,7 @@
#include <new> // for bad_alloc, placement new
#include <stdarg.h> // for va_arg(), va_list
#include <string.h> // for memset()
+#include <rw_exception.h> // for _rw_throw_exception
/**************************************************************************/
@@ -55,37 +56,7 @@
static int _rw_id_gen; // generates unique ids
-/**************************************************************************/
-_TEST_EXPORT int
-rw_vasnprintf (char**, size_t*, const char*, va_list);
-
-
-static void
-_rw_throw_exception (const char *file, int line, const char *fmt, ...)
-{
- struct BadSharedAlloc: _RWSTD_BAD_ALLOC {
- char what_ [4096];
-
- /* virtual */ const char* what () const _THROWS (()) {
- return what_;
- }
- };
-
- BadSharedAlloc ex;
-
- va_list va;
- va_start (va, fmt);
-
- char *buf = ex.what_;
- size_t bufsize = sizeof ex.what_;
- rw_vasnprintf (&buf, &bufsize, fmt, va);
-
- va_end (va);
-
- throw ex;
-}
-
/**************************************************************************/
SharedAlloc::
@@ -136,14 +107,14 @@
const size_t nbytes = nelems * size;
if (max_blocks_ < n_blocks_ + nelems)
- _rw_throw_exception (__FILE__, __LINE__,
- "allocate (%zu): reached block limit of %zu",
- nbytes, max_blocks_);
+ rw_throw (ex_bad_alloc, __FILE__, __LINE__, 0,
+ "allocate (%zu): reached block limit of %zu",
+ nbytes, max_blocks_);
if (max_bytes_ < n_bytes_ + nbytes)
- _rw_throw_exception (__FILE__, __LINE__,
- "allocate (%zu): reached size limit of %zu",
- nbytes, max_bytes_);
+ rw_throw (ex_bad_alloc, __FILE__, __LINE__, 0,
+ "allocate (%zu): reached size limit of %zu",
+ nbytes, max_bytes_);
return operator_new (nbytes, false);
}
@@ -229,9 +200,9 @@
// increment the exception counter for this function
++n_throws_ [mf];
- _rw_throw_exception (__FILE__, __LINE__,
- "UserAlloc::%s: reached call limit of %zu",
- _rw_func_names [mf], throw_at_calls_);
+ rw_throw (ex_bad_alloc, __FILE__, __LINE__, 0,
+ "UserAlloc::%s: reached call limit of %zu",
+ _rw_func_names [mf], throw_at_calls_);
RW_ASSERT (!"logic error: should not reach");
}
Index: new.cpp
===================================================================
--- new.cpp (revision 421257)
+++ new.cpp (working copy)
@@ -28,8 +28,6 @@
// expand _TEST_EXPORT macros
#define _RWSTD_TEST_SRC
-#include <new> // for bad_alloc
-
#include <stdlib.h> // for abort(), free(), getenv(), malloc()
#include <string.h> // for memset()
@@ -37,6 +35,8 @@
#include <rw_printf.h>
#include <rw_new.h>
+#include <rw_exception.h>
+
/************************************************************************/
#ifndef _RWSTD_BAD_ALLOC
@@ -272,16 +272,6 @@
}
-struct BadAlloc: _RWSTD_BAD_ALLOC
-{
- char what_ [4096];
-
- /* virtual */ const char* what () const _THROWS (()) {
- return what_;
- }
-};
-
-
static size_t seq_gen; // sequence number generator
@@ -365,48 +355,47 @@
|| reached_block_limit
|| reached_size_limit) {
- BadAlloc ex;
-
#ifndef _RWSTD_NO_EXCEPTIONS
- rw_snprintfa (ex.what_, 4096U, "%s:%d: %s (%zu) ",
- __FILE__, __LINE__, name [array], nbytes);
+ try {
+ const char * threw = "threw bad_alloc:";
- strcat (ex.what_, "threw bad_alloc: ");
+ if (reached_call_limit)
+ rw_throw (ex_bad_alloc,
+ __FILE__, __LINE__, name [array],
+ "(%zu) %s reached call limit of %zu",
+ nbytes, threw, pst->new_calls_ [array]);
- if (reached_call_limit)
+ else if (reached_block_limit)
+ rw_throw (ex_bad_alloc,
+ __FILE__, __LINE__, name [array],
+ "(%zu) %s reached block limit of %zu: %zu",
+ nbytes, threw, *pst->throw_at_blocks_ [array],
+ pst->blocks_ [array]);
- rw_snprintfa (
- ex.what_ + strlen (ex.what_), 4096U - strlen (ex.what_),
- "reached call limit of %zu", pst->new_calls_ [array]);
+ else if (reached_size_limit)
+ rw_throw (ex_bad_alloc,
+ __FILE__, __LINE__, name [array],
+ "(%zu) %s reached size limit of %zu: %zu",
+ nbytes, threw, *pst->throw_at_bytes_ [array],
+ pst->bytes_ [array]);
+ }
+ catch (const std::exception & ex) {
+ if (trace_sequence [0] <= seq_gen && seq_gen < trace_sequence [1])
+ rw_fprintf (rw_stderr, "%s\n", ex.what ());
- else if (reached_block_limit)
+ throw;
+ }
- rw_snprintfa (
- ex.what_ + strlen (ex.what_), 4096U - strlen (ex.what_),
- "reached block limit of %zu: %zu",
- *pst->throw_at_blocks_ [array], pst->blocks_ [array]);
-
- else if (reached_size_limit)
-
- rw_snprintfa (
- ex.what_ + strlen (ex.what_), 4096U - strlen (ex.what_),
- "reached size limit of %zu: %zu",
- *pst->throw_at_bytes_ [array], pst->bytes_ [array]);
-
- if (trace_sequence [0] <= seq_gen && seq_gen < trace_sequence [1])
- rw_fprintf (rw_stderr, "%s\n", ex.what ());
-
- throw ex;
-
#else // if defined (_RWSTD_NO_EXCEPTIONS)
if (reached_breakpoint) {
- rw_snprintfa (
- ex.what_ + strlen (ex.what_), 4096U - strlen (ex.what_),
+ char buf[4096];
+
+ rw_snprintfa (buf, sizeof(buf),
"reached a breakpoint at of %zu calls", *pst->break_at_seqno_);
- _RW::__rw_assert_fail (ex.what_, __FILE__, __LINE__, name [array]);
+ _RW::__rw_assert_fail (buf, __FILE__, __LINE__, name [array]);
}
if (trace_sequence [0] <= seq_gen && seq_gen < trace_sequence [1])
@@ -426,16 +415,21 @@
ptr = malloc (block_size);
if (!ptr) {
- BadAlloc ex;
- rw_snprintfa (ex.what_, 4096U,
- "%s:%d: %s (%zu) threw bad_alloc: malloc() returned 0",
- __FILE__, __LINE__, name [array], nbytes);
-
- if (trace_sequence [0] <= seq_gen && seq_gen < trace_sequence [1])
- rw_fprintf (rw_stderr, "%s\n", ex.what ());
#ifndef _RWSTD_NO_EXCEPTIONS
- throw ex;
+
+ try {
+ rw_throw (ex_bad_alloc,
+ __FILE__, __LINE__, name [array],
+ "(%zu): malloc() returned 0", nbytes);
+ }
+ catch (const std::exception & ex) {
+ if (trace_sequence [0] <= seq_gen && seq_gen < trace_sequence [1])
+ rw_fprintf (rw_stderr, "%s\n", ex.what ());
+
+ throw;
+ }
+
#else // if defined (_RWSTD_NO_EXCEPTIONS)
return ptr;
#endif // _RWSTD_NO_EXCEPTIONS
@@ -514,7 +508,6 @@
Header* const hdr = _rw_find_block (ptr, true, name [array]);
if (!hdr) {
-
// hdr should never be 0 except under special circumstances
// such as when the compiler's runtime library itself passes
// the wrong argument to operator delete (such as libcxx
Index: rw_streambuf.h
===================================================================
--- rw_streambuf.h (revision 421257)
+++ rw_streambuf.h (working copy)
@@ -29,11 +29,12 @@
#define RW_STREAMBUF_H_INCLUDED
-#include <cstring> // for memset()
-#include <streambuf> // for basic_streambuf
+#include <cstring> // for memset()
+#include <streambuf> // for basic_streambuf
#include <testdefs.h>
-#include <rw_char.h> // for make_char
+#include <rw_char.h> // for make_char
+#include <rw_exception.h> // for _rw_throw_exception
enum MemFun {
@@ -56,6 +57,10 @@
Unknown = 0x4000
};
+static const char* const streambuf_func_names[] = {
+ "setbuf", "seekoff", "seekpos", "showmanyc", "xsgetn", "underflow",
+ "uflow", "overflow", "pbackfail", "xsputn", "sync"
+};
template <class charT, class Traits>
struct MyStreambuf: std::basic_streambuf<charT, Traits>
@@ -163,17 +168,21 @@
public:
int ncalls (MemFun) const;
+ int memfun_inx (MemFun) const;
char_type *buf_;
std::streamsize bufsize_;
- int throw_set_; // functions that should throw
- int fail_set_; // functions that should fail
- MemFun threw_; // which function threw
- MemFun failed_; // which function failed
+ int throw_set_; // functions that should throw
+ int fail_set_; // functions that should fail
+ MemFun threw_; // which function threw
+ MemFun failed_; // which function failed
- int fail_when_; // call number on which to fail
+ int fail_when_; // call number on which to fail
+ int throw_when_ [11]; // call number on which to throw for each func
+ int allthrows_; // total number of thrown exceptions
+
// max size of the pending input sequence
static std::streamsize in_pending_;
@@ -183,7 +192,10 @@
private:
bool test (MemFun) const;
+
int ncalls_ [11]; // number of calls made to each function
+
+ int allcalls_; // total number of calls
};
@@ -202,11 +214,14 @@
MyStreambuf (std::streamsize bufsize, int fail_set, int when)
: Base (), buf_ (0), bufsize_ (bufsize),
throw_set_ (0), fail_set_ (0), threw_ (None), failed_ (None),
- fail_when_ (when)
+ fail_when_ (when), allthrows_ (0), allcalls_ (0)
{
// reset the member function call counters
std::memset (ncalls_, 0, sizeof ncalls_);
+ // reset the member function throw counters
+ std::memset (throw_when_, 0, sizeof throw_when_);
+
// allocate a (possibly wide) character buffer for output
buf_ = new charT [bufsize_];
@@ -232,11 +247,14 @@
MyStreambuf (const char *buf, std::streamsize bufsize, int fail_set, int when)
: Base (), buf_ (0), bufsize_ (bufsize),
throw_set_ (0), fail_set_ (0), threw_ (None), failed_ (None),
- fail_when_ (when)
+ fail_when_ (when), allthrows_ (0), allcalls_ (0)
{
// reset the member function call counters
std::memset (ncalls_, 0, sizeof ncalls_);
+ // reset the member function throw counters
+ std::memset (throw_when_, 0, sizeof throw_when_);
+
// as a convenience, if `bufsize == -1' compute the size
// from the length of `buf'
if (std::streamsize (-1) == bufsize_)
@@ -396,11 +414,10 @@
return traits_type::eof ();
}
-
template <class charT, class Traits>
int
MyStreambuf<charT, Traits>::
-ncalls (MemFun which) const
+memfun_inx (MemFun which) const
{
int inx = -1;
@@ -413,10 +430,22 @@
}
}
- return ncalls_ [inx];
+ return inx;
}
+template <class charT, class Traits>
+int
+MyStreambuf<charT, Traits>::
+ncalls (MemFun which) const
+{
+ int inx = memfun_inx (which);
+ if (0 <= inx)
+ return ncalls_ [inx];
+
+ return -1;
+}
+
template <class charT, class Traits>
bool
MyStreambuf<charT, Traits>::
@@ -424,35 +453,34 @@
{
MyStreambuf* const self = _RWSTD_CONST_CAST (MyStreambuf*, this);
- int inx = -1;
+ int inx = memfun_inx (which);
+ if (-1 == inx)
+ return true;
- for (unsigned i = 0; i < sizeof (which) * _RWSTD_CHAR_BIT; ++i) {
- if (which & (1U << i)) {
- if (inx < 0)
- inx = i;
- else
- return true;
- }
- }
-
// increment the counter tracking the number of calls made
// to each member function; do so regardless of whether
// an exception will be thrown below
- const int callno = self->ncalls_ [inx]++;
+ self->ncalls_ [inx] ++;
+ self->allcalls_ ++;
+ const int callno = self->ncalls_ [inx];
#ifndef _RWSTD_NO_EXCEPTIONS
// if the call counter is equal to the `fail_when_' watermark
// and `shich' is set in the `throw_set_' bitmask, throw an
// exception with the value of the member id
- if (callno == fail_when_ && throw_set_ & which) {
+ if (callno == throw_when_ [inx] && throw_set_ & which) {
self->threw_ = which;
- throw which;
+ self->allthrows_++;
+
+ rw_throw (ex_stream, __FILE__, __LINE__,
+ streambuf_func_names [inx],
+ "%s", "test exception");
}
#else // if defined (_RWSTD_NO_EXCEPTIONS)
- if (callno == fail_when_ && throw_set_ & which) {
+ if (callno == throw_when_ [inx] && throw_set_ & which) {
self->threw_ = which;
return false;
}
/************************************************************************
*
* rw_exception.h - defines a test driver exception
*
* $Id: $
*
***************************************************************************
*
* Copyright 2006 The Apache Software Foundation or its licensors,
* as applicable.
*
* Copyright 2004-2006 Rogue Wave Software.
*
* Licensed 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.
*
**************************************************************************/
#ifndef RW_EXCEPTION_H_INCLUDED
#define RW_EXCEPTION_H_INCLUDED
#include <testdefs.h>
enum ExceptionId
{
ex_unknown = 0,
// custom exceptions, i.e. not derived for std::exception
ex_stream = 1,
ex_custom = 7,
// exceptions derived from std::exception
ex_bad_alloc = 8,
ex_std = (7 << 3)
};
struct _TEST_EXPORT Exception
{
const ExceptionId id_;
Exception (ExceptionId id) : id_ (id) {}
virtual ~Exception ();
virtual const char* what () const = 0;
};
_TEST_EXPORT void
rw_throw (ExceptionId exid, const char *file, int line,
const char *function, const char *fmt, ...);
#endif // RW_EXCEPTION_H_INCLUDED
/************************************************************************
*
* exception.cpp - exceptions testsuite helpers
*
* $Id: $
*
************************************************************************
*
* Copyright 2006 The Apache Software Foundation or its licensors,
* as applicable.
*
* Copyright 2006 Rogue Wave Software.
*
* Licensed 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.
*
**************************************************************************/
// expand _TEST_EXPORT macros
#define _RWSTD_TEST_SRC
#include <rw_exception.h>
#include <driver.h>
#include <rw_printf.h>
#include <string.h> // for strncpy()
#include <malloc.h> // for free()
#include <stddef.h> // for size_t
#include <stdarg.h> // for va_arg(), va_list
#include <new> // for std::bad_alloc
/**************************************************************************/
Exception::~Exception ()
{
}
/**************************************************************************/
struct ExceptionBase
{
char buf_ [256];
char* what_;
ExceptionBase ();
ExceptionBase (const ExceptionBase&);
~ExceptionBase ();
ExceptionBase& operator= (const ExceptionBase&);
private:
void free_ ();
};
ExceptionBase::ExceptionBase () : what_ (buf_)
{
buf_ [0] = '\0';
}
ExceptionBase::ExceptionBase (const ExceptionBase& ex) : what_ (buf_)
{
*this = ex;
}
ExceptionBase::~ExceptionBase ()
{
free_ ();
}
ExceptionBase& ExceptionBase::operator= (const ExceptionBase& ex)
{
if (&ex != this) {
free_ ();
if (ex.buf_ != ex.what_)
what_ = strdup (ex.what_);
else
strncpy (buf_, ex.buf_, sizeof (buf_));
}
return *this;
}
void ExceptionBase::free_ ()
{
if (buf_ != what_) {
free (what_);
what_ = buf_;
}
}
/**************************************************************************/
struct BadAlloc : std::bad_alloc, ExceptionBase
{
~BadAlloc () _THROWS (()) {}
const char* what () const _THROWS (())
{
return what_;
}
};
/**************************************************************************/
struct StreamException : Exception, ExceptionBase
{
StreamException () : Exception (ex_stream)
{
}
const char* what () const
{
return what_;
}
};
/**************************************************************************/
_TEST_EXPORT int
rw_vasnprintf (char**, size_t*, const char*, va_list);
/**************************************************************************/
static int
_rw_format (char** pbuf, size_t* pbufsize, const char* file, int line,
const char* function, const char* fmt, va_list va)
{
const int nchars1 = function ?
rw_asnprintf (pbuf, pbufsize, "\"%s\", %d, \"%s\" ",
file, line, function) :
rw_asnprintf (pbuf, pbufsize, "\"%s\", %d, ", file, line);
if (0 > nchars1)
return nchars1;
char *tmpbuf = 0;
size_t tmpsize = 0;
const int nchars2 = rw_vasnprintf (&tmpbuf, &tmpsize, fmt, va);
if (0 > nchars2) {
free (tmpbuf);
return nchars1;
}
const int nchars3 = rw_asnprintf (pbuf, pbufsize, "%{+}%s", tmpbuf);
free (tmpbuf);
return 0 > nchars3 ? nchars1 : nchars1 + nchars3;
}
// str should be allocated by malloc() call
// str shouldn't be free after call _rw_init_exception()
static void _rw_init_exception (ExceptionBase& ex, char* str, size_t nchars)
{
if (nchars < sizeof (ex.buf_)) {
memcpy (ex.buf_, str, nchars);
ex.buf_ [nchars] = '\0';
free (str);
}
else
ex.what_ = str;
}
_TEST_EXPORT void
rw_throw (ExceptionId exid, const char *file, int line,
const char *function, const char *fmt, ...)
{
// format string
char* buf = 0;
size_t bufsize = 0;
va_list va;
va_start (va, fmt);
const int nchars = _rw_format (&buf, &bufsize, file, line,
function, fmt, va);
va_end (va);
switch (exid) {
case ex_stream:
{
StreamException ex;
_rw_init_exception (ex, buf, nchars);
throw ex;
}
case ex_bad_alloc:
{
BadAlloc ex;
_rw_init_exception (ex, buf, nchars);
throw ex;
}
default:
free (buf);
throw ex_unknown;
}
}