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;
    }
}

Reply via email to