| /*************************************************************************** |
| * |
| * 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 <rw_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); |
| } |