blob: bf02a51415b3a562a832eb046eaa1baa0373f229 [file] [log] [blame]
/***************************************************************************
*
* 19.std.exceptions.cpp - test exercising [lib.std.exceptions]
*
* $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 2001-2008 Rogue Wave Software.
*
**************************************************************************/
#include <stdexcept>
/**************************************************************************/
template <class Exception>
int test_ex_spec (Exception*, const char *str)
{
#ifndef _RWSTD_NO_EXCEPTIONS
try {
// also tests that exception objects can be constructed
// from a const char* argument w/o <string> having been
// explicitly #included first
Exception e (str);
(void)&e;
}
catch (...) {
return 1;
}
#else // if defined (_RWSTD_NO_EXCEPTIONS);
_RWSTD_UNUSED (str);
#endif // _RWSTD_NO_EXCEPTIONS
return 0;
}
/**************************************************************************/
#include <string>
/**************************************************************************/
// helpers to verify that each exception's ctor is explicit
// not defined since they must not be referenced if test is successful
void is_explicit (const std::logic_error&);
void is_explicit (const std::domain_error&);
void is_explicit (const std::invalid_argument&);
void is_explicit (const std::length_error&);
void is_explicit (const std::out_of_range&);
void is_explicit (const std::runtime_error&);
void is_explicit (const std::range_error&);
void is_explicit (const std::overflow_error&);
void is_explicit (const std::underflow_error&);
struct bogus_exception
{
// also verifies that std::string is declared
bogus_exception (const std::string&) { }
bogus_exception (const char*) { }
};
// calls to the overoaded is_explicit (std::string ()) must resolve
// to this function since there must be no implicit conversion from
// std::string to any of the exception classes
void is_explicit (const bogus_exception&) { }
// exercise the ability to construct exception objects w/o
// explicitly #including <string> first; std::string must still
// be declared (but not necessarily defined)
#if !defined (_RWSTD_NO_NAMESPACE) && !defined (_RWSTD_NO_HONOR_STD)
// declare a global function with the same name as exception
# define TEST_NAMESPACE_DEF(T) void T ()
#else
# define TEST_NAMESPACE_DEF(ignore) (void)0
#endif // !_RWSTD_NO_NAMESPACE && !_RWSTD_NO_HONOR_STD
#ifndef _RWSTD_NO_PTR_EXCEPTION_SPEC
# define _PTR_THROWS(spec) _THROWS (spec)
#else // if defined (_RWSTD_NO_PTR_EXCEPTION_SPEC)
// throw specs on pointers to functions not implemented...
# define _PTR_THROWS(ignore)
#endif // _RWSTD_NO_PTR_EXCEPTION_SPEC
// verify that each name is declared in namespace std
// w/o polluting the global namespace
#define TEST_DEF(T) \
typedef std::T T ## _type; \
TEST_NAMESPACE_DEF (T); \
/* construct an object */ \
std::T obj_ ## T ("std::" _RWSTD_STR (T)); \
/* assign the address of object to std::exception* */ \
e = &obj_ ## T; \
/* verify that assignment can't throw */ \
std::T& (std::T::*p_assign_ ## T)(const std::T&) \
_PTR_THROWS(()) = &std::T::operator=; \
_RWSTD_UNUSED (p_assign_ ## T); \
/* verify that what() can't throw */ \
const char* (std::T::*p_what_ ## T)() const \
_PTR_THROWS(()) = &std::T::what; \
_RWSTD_UNUSED (p_what_ ## T); \
/* verify ctor postcondition */ \
result = result << 1 | cmp (obj_ ## T.what (), \
"std::" _RWSTD_STR (T))
int cmp (const char *s1, const char *s2)
{
for (; *s1 && *s1 == *s2; ++s1, ++s2);
return *s2 - *s1;
}
// returns 0 on success; on failure returns a bitmap with one bit set
// for every exception that failed its ctor postcondition
static int test_exception_defs ()
{
int result = 0;
// used below to verify public inheritance from std::exception
std::exception *e = 0;
TEST_DEF (logic_error);
TEST_DEF (domain_error);
TEST_DEF (invalid_argument);
TEST_DEF (length_error);
TEST_DEF (out_of_range);
TEST_DEF (runtime_error);
TEST_DEF (range_error);
TEST_DEF (overflow_error);
TEST_DEF (underflow_error);
_RWSTD_UNUSED (e);
#ifndef _RWSTD_NO_EXPLICIT
// verify that each exceptions converting ctor is explicit
// use a pointer since std::string need not be a complete class
const char s[40] = "";
const std::string *ps = _RWSTD_REINTERPRET_CAST (const std::string*, s);
is_explicit (*ps);
// verify that each exceptions converting ctor from const char*
// (if one exists) is also explicit
is_explicit (s);
#endif // _RWSTD_NO_EXPLICIT
return result;
}
#undef _PTR_THROWS
/**************************************************************************/
#include <rw_new.h>
#include <driver.h>
#include <cstddef>
/**************************************************************************/
template <class Exception>
int test_throw (Exception*, const char *str)
{
#ifndef _RWSTD_NO_EXCEPTIONS
//////////////////////////////////////////////////////////////////
try {
throw Exception (str);
}
catch (const Exception &e) {
// caught by const reference
rw_assert (e.what () && !cmp (e.what (), str),
0, __LINE__,
"caught by const reference; %s::what() == %#s",
str, str);
}
catch (...) {
rw_assert (false, 0, __LINE__,
"threw %s, caught an unknown exception", str);
}
//////////////////////////////////////////////////////////////////
try {
throw Exception (str);
}
catch (Exception e) {
// caught by value
rw_assert (e.what () && !cmp (e.what (), str),
0, __LINE__,
"caught by value; %s::what() == %#s",
str, str);
}
catch (...) {
rw_assert (false, 0, __LINE__,
"threw %s, caught an unknown exception", str);
}
//////////////////////////////////////////////////////////////////
const Exception ex (str);
try {
throw ex;
}
catch (Exception e) {
// caught copy by value
rw_assert (e.what () && !cmp (e.what (), str),
0, __LINE__,
"caught copy by value; %s::what() == %#s",
str, str);
}
catch (...) {
rw_assert (false, 0, __LINE__,
"threw %s, caught an unknown exception", str);
}
//////////////////////////////////////////////////////////////////
try {
throw ex;
}
catch (std::exception &e) {
// caught by non-const reference to a base class
rw_assert (e.what () && !cmp (e.what (), str),
0, __LINE__,
"caught by non-const reference to base; %s::what() == %#s",
str, str);
}
catch (...) {
rw_assert (false, 0, __LINE__,
"threw %s, caught an unknown exception", str);
}
//////////////////////////////////////////////////////////////////
try {
try {
throw Exception (str);
}
catch (...) {
// rethrown
throw;
}
}
catch (Exception e) {
// rethrown object caught by value
rw_assert (e.what () && !cmp (e.what (), str),
0, __LINE__,
"caught rethrown by value; %s::what() == %#s", str, str);
}
catch (...) {
rw_assert (false, 0, __LINE__,
"threw %s, caught an unknown exception", str);
}
//////////////////////////////////////////////////////////////////
try {
try {
throw Exception (str);
}
catch (Exception e) {
rw_assert (e.what () && !cmp (e.what (), str),
0, __LINE__,
"caught by value; %s::what() == %#s",
str, str);
// rethrown by value
throw e;
}
}
catch (Exception e) {
// rethrown object caught by value
rw_assert (e.what () && !cmp (e.what (), str),
0, __LINE__,
"caught rethrown copy by value; %s::what() == %#s",
str, str);
}
catch (...) {
rw_assert (false, 0, __LINE__,
"threw %s, caught an unknown exception", str);
}
#else // if defined (_RWSTD_NO_EXCEPTIONS);
_RWSTD_UNUSED (t);
_RWSTD_UNUSED (str);
#endif // _RWSTD_NO_EXCEPTIONS
return 0;
}
/**************************************************************************/
static int
run_test (int, char* [])
{
// verify that exception's (and its derivatives') ctor is explicit
is_explicit (std::string ());
const char* const names[] = {
"logic_error", "domain_error", "invalid_argument", "length_error",
"out_of_range", "runtime_error", "range_error", "overflow_error",
"underflow_error"
};
// if set, each bit corresponds to a failed exception assertion
const int failures = test_exception_defs ();
for (unsigned i = 0; i != sizeof names / sizeof *names; ++i) {
rw_assert (!(failures & (1 << i)), 0, __LINE__,
"std::%s::%s (const std::string&) postcondition",
names [i]);
}
// have replacement operator new throw an exception
rwt_free_store* const pst = rwt_get_free_store (0);
*pst->throw_at_calls_ [0] = pst->new_calls_ [0] + 1;
// create a very long string to guarantee that exception ctors
// try to dynamically allocate storage even if it otherwise
// use some clever scheme to avoid doing so
char long_str [0x10000];
for (unsigned i = 0; i != sizeof long_str - 1; ++i)
long_str [i] = '*';
long_str [sizeof long_str - 1] = '\0';
int new_throws = 0;
_TRY {
// see if replacement operator new throws an exception
// when called directly from the program
void *p = ::operator new (sizeof long_str);
::operator delete (p);
new_throws = 0;
#ifdef _RWSTD_NO_REPLACEABLE_NEW_DELETE
// MSVC and VAC++ don't reliably replace operators
// new and delete across shared librray boundaries
rw_warn (false, 0, __LINE__,
"replacement ::operator new(std::size_t = %u) failed "
"to throw when called directly from a program: this "
"is an expected failure on this platform",
sizeof long_str);
#else // if !defined (_RWSTD_NO_REPLACEABLE_NEW_DELETE)
rw_assert (false, 0, __LINE__,
"replacement ::operator new(std::size_t = %u) "
"unexpectdly failed to throw when called directly "
"from a program",
sizeof long_str);
#endif // _RWSTD_NO_REPLACEABLE_NEW_DELETE
}
_CATCH (...) {
new_throws = 1;
}
*pst->throw_at_calls_ [0] = pst->new_calls_ [0] + 1;
_TRY {
// see if replacement operator new throws an exception
// when called indirectly, i.e., from the library binary
void* const p = _RW::__rw_allocate (sizeof long_str, 0);
_RW::__rw_deallocate (p, 0);
new_throws = 0;
#ifdef _RWSTD_NO_REPLACEABLE_NEW_DELETE
// MSVC and VAC++ don't reliably replace operators
// new and delete across shared librray boundaries
rw_warn (false, 0, __LINE__,
"replacement ::operator new(std::size_t = %u) failed "
"to throw when called from the library: this is an "
"expected failure on this platform",
sizeof long_str);
#else // if !defined (_RWSTD_NO_REPLACEABLE_NEW_DELETE)
rw_assert (false, 0, __LINE__,
"replacement ::operator new(std::size_t = %u) "
"unexpectdly failed to throw when called from "
"the library",
sizeof long_str);
#endif // _RWSTD_NO_REPLACEABLE_NEW_DELETE
}
_CATCH (...) {
new_throws = 1;
}
// exercise exception specification on class ctors
// and the ability to throw and catch exception objects
#define TEST_EX_SPEC(T) do { \
/* do not induce an exception from replacement operator new */ \
*pst->throw_at_calls_ [0] = std::size_t (-1); \
*pst->throw_at_calls_ [1] = std::size_t (-1); \
/* verify that exception objects can be thrown, caught, and rethrown */\
test_throw ((std:: T*)0, "std::" #T); \
/* induce an exception from replacement operator new */ \
*pst->throw_at_calls_ [0] = pst->new_calls_ [0] + 1; \
*pst->throw_at_calls_ [1] = pst->new_calls_ [1] + 1; \
/* verify that each exception ctor propagates the exception thrown */ \
/* from operator new(); failure wil cause a call to terminate() */ \
/* tests are expected to silently fail if (0 == new_throws) holds */ \
const int threw = test_ex_spec ((std::T*)0, long_str); \
rw_assert (threw == new_throws, 0, __LINE__, \
"attempthing to construct a std::" #T " %s an exception", \
new_throws ? "failed to rethrow" : "unexpectedly threw"); \
} while (0)
TEST_EX_SPEC (logic_error);
TEST_EX_SPEC (domain_error);
TEST_EX_SPEC (invalid_argument);
TEST_EX_SPEC (length_error);
TEST_EX_SPEC (out_of_range);
TEST_EX_SPEC (runtime_error);
TEST_EX_SPEC (range_error);
TEST_EX_SPEC (overflow_error);
TEST_EX_SPEC (underflow_error);
return 0;
}
/**************************************************************************/
int main (int argc, char* argv [])
{
return rw_test (argc, argv, __FILE__,
"lib.std.exceptions",
0 /* no comment */,
run_test,
"",
(void*)0);
}