| /*************************************************************************** |
| * |
| * 18.support.dynamic.cpp - test exercising [lib.support.dynamic] |
| * |
| * $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 <new> |
| #include <memory> |
| |
| #include <rw_driver.h> |
| |
| #include <rw/_defs.h> |
| #include <rw/_error.h> |
| |
| #include <cstdlib> // for malloc() and free() |
| #include <cstring> // for strlen() |
| |
| |
| #ifdef _MSC_VER |
| // verify that <new> et al can coexist with MSVC's <new.h> |
| # include <new.h> |
| #endif |
| |
| /**************************************************************************/ |
| |
| // detects recursive implementation of operator new (size_t, nothrow_t) |
| static int recursive_new_nothrow = 0; |
| static int recursive_delete_nothrow = 0; |
| |
| // MemoryAllocator is used to replace the global new and delete |
| // in order to test conformance of of the other operators that |
| // might be defined in <new> |
| |
| struct MemoryAllocator |
| { |
| MemoryAllocator () { |
| init (); |
| } |
| |
| void* operatorNew (std::size_t n) _THROWS ((std::bad_alloc)) { |
| _allocPtr = 0; |
| _newCalled = true; |
| _bytesRequested = n; |
| if (_failAlloc) { |
| _failAlloc = false; |
| _THROW (std::bad_alloc ()); |
| } |
| #if !defined (_RWSTD_NO_EXT_OPERATOR_NEW) && \ |
| defined (_RWSTD_NO_OPERATOR_NEW_NOTHROW) |
| |
| // test our implementation of |
| // ::operator new(size_t, const std::nothrow) |
| // this operator must not be implemented in terms |
| // of ::operator new(size_t) to prevent recursion |
| |
| if (recursive_new_nothrow++) |
| _allocPtr = std::malloc (_bytesRequested); |
| else |
| _allocPtr = ::operator new (_bytesRequested, std::nothrow); |
| |
| --recursive_new_nothrow; |
| |
| #else |
| _allocPtr = std::malloc (_bytesRequested); |
| #endif |
| return _allocPtr; |
| } |
| |
| void operatorDelete (void* ptr) _THROWS (()) { |
| _deallocPtr = ptr; |
| #if !defined (_RWSTD_NO_EXT_OPERATOR_NEW) && \ |
| defined (_RWSTD_NO_OPERATOR_NEW_NOTHROW) |
| // memory allocated by our nothrow operator new() |
| if (recursive_delete_nothrow++ || recursive_new_nothrow) |
| std::free (ptr); |
| else |
| ::operator delete (ptr, std::nothrow); |
| |
| --recursive_delete_nothrow; |
| #else |
| std::free (ptr); |
| #endif |
| _deleteCalled = true; |
| } |
| |
| void init () { |
| _newCalled = _deleteCalled = _failAlloc = false; |
| _bytesRequested = 0; |
| _deallocPtr = _allocPtr = 0; |
| } |
| |
| bool newCalled () { |
| bool tmp = _newCalled; |
| _newCalled = 0; |
| return tmp; |
| } |
| |
| bool deleteCalled () { |
| bool tmp = _deleteCalled; |
| _deleteCalled = 0; |
| return tmp; |
| } |
| |
| std::size_t _bytesRequested; |
| bool _newCalled; |
| bool _deleteCalled; |
| bool _failAlloc; |
| void* _allocPtr; |
| void* _deallocPtr; |
| }; |
| |
| |
| // static instance of memory allocator |
| static MemoryAllocator alloc; |
| |
| /**************************************************************************/ |
| |
| |
| // replace the global operators with MemoryAllocator versions |
| void* operator new (std::size_t size) _THROWS ((std::bad_alloc)) |
| { |
| return alloc.operatorNew (size); |
| } |
| |
| void operator delete (void* p) _THROWS (()) |
| { |
| alloc.operatorDelete (p); |
| } |
| |
| /**************************************************************************/ |
| |
| |
| #define TEST_OP_THROWS(expr,cond,tag) \ |
| _TRY { \ |
| alloc._failAlloc = true; \ |
| expr; \ |
| rw_assert (cond, 0, __LINE__, tag); \ |
| } \ |
| _CATCH (std::bad_alloc) { \ |
| RW_ASSERT (!cond, 0, __LINE__, tag); \ |
| } \ |
| _CATCH (...) { \ |
| rw_assert (!cond, 0, __LINE__, tag); \ |
| } \ |
| (void)0 |
| |
| /**************************************************************************/ |
| |
| |
| // provide a handler for unexpected exceptions so that at least the |
| // test will log the event rather than just aborting |
| void my_unexpected_handler () |
| { |
| rw_assert (false, 0, __LINE__, "caught an unexpected exception\n"); |
| } |
| |
| // used in exists_set_new_handler below |
| void my_new_handler () |
| { |
| } |
| |
| // grab default throw procedure |
| void (*default_throw_proc)(int, char*) = _RW::__rw_throw_proc; |
| |
| bool my_throw_proc_called = false; |
| |
| // my_throw_proc just sets a global flag to indicate |
| // that it was called, and then dispatches the call |
| // to the default throw procedure |
| static void my_throw_proc (int id, char* what) |
| { |
| my_throw_proc_called = true; |
| default_throw_proc (id, what); |
| } |
| |
| /**************************************************************************/ |
| |
| |
| static |
| int run_test (int, char* []) |
| { |
| std::set_unexpected (&my_unexpected_handler); |
| |
| if (1) { |
| // exists_std_nothrow |
| const std::nothrow_t* p = &std::nothrow; |
| rw_assert (p != 0, 0, __LINE__, "std::nothrow defined"); |
| } |
| |
| if (1) { |
| // exists_bad_alloc |
| std::bad_alloc ba1; // default ctor |
| std::bad_alloc ba2 = ba1; // copy ctor |
| |
| std::exception* e = &ba1; // derived from exception |
| (void) &e; |
| ba1 = ba2; // assignment operator |
| |
| // verify that a member function is accessible and has the |
| // appropriate signature, including return type and exception |
| // specification |
| |
| std::bad_alloc& (std::bad_alloc::*p_op_assign) |
| (const std::bad_alloc&) _PTR_THROWS (()) |
| = &std::bad_alloc::operator=; |
| _RWSTD_UNUSED (p_op_assign); |
| |
| const char* |
| (std::bad_alloc::*p_what_fn)() const _PTR_THROWS (()) |
| = &std::bad_alloc::what; |
| _RWSTD_UNUSED (p_what_fn); |
| |
| const char* s = ba1.what (); |
| rw_assert (s && 0 != std::strlen (s), 0, __LINE__, |
| "std::bad_alloc::what() returned bad string"); |
| } |
| |
| if (1) { |
| // set_new_handler |
| typedef void (*new_handler_t) (); |
| |
| new_handler_t (*f) (new_handler_t) _PTR_THROWS (()); |
| f = &std::set_new_handler; |
| |
| rw_assert (f != 0, 0, __LINE__, |
| "std::set_new_handler() defined"); |
| |
| new_handler_t original_handler |
| = std::set_new_handler (my_new_handler); |
| |
| #if !defined (__EDG__) || __EDG_VERSION__ > 245 || defined (__DECCXX) |
| |
| // EDG eccp 2.45 standalone demo is known to fail |
| rw_assert (original_handler == 0, 0, __LINE__, |
| "std::set_new_handler() unexpectedly returned " |
| "installed handler"); |
| |
| #define _RWSTD_NO_EXT_OPERATOR_NEW |
| |
| #endif // !__EDG__ || __EDG_VERSION__ > 245 || __DECCXX |
| |
| new_handler_t replacement_handler |
| = std::set_new_handler (original_handler); |
| rw_assert (my_new_handler == replacement_handler, 0, __LINE__, |
| "std::set_new_handler() failed to return previously " |
| "installed handler"); |
| } |
| |
| // test all operators that we have provided definitions for |
| // in <new>; exercises the operators by setting the |
| // replacement allocator to fail, and asserting that an |
| // exception is thrown or not as appropriate for the operator |
| if (1) { |
| // operator new throws |
| void* ptr = 0; |
| _RWSTD_UNUSED (ptr); |
| |
| alloc.init (); // reset the allocator |
| |
| #ifdef _RWSTD_NO_OPERATOR_NEW_NOTHROW |
| |
| // verify that the nothrow version of operator new provided |
| // by our library doesn't call the ordinary operator new |
| // (if it did it would cause recursion if a replacement operator |
| // new were to be implemented in terms of the nothrow version) |
| |
| // also verify that it returns 0 on failure |
| |
| // force a failure by requesting too large a block of storage |
| // size_t (~0) alone isn't good enough since it causes annoying |
| // dialog boxes to be popped up by the MSVCRTD, the MSVC runtime |
| TEST_OP_THROWS ((ptr = operator new (~0U - 4096U, std::nothrow)), |
| (ptr == 0), |
| "operator new(size_t, nothrow_t) returns 0"); |
| |
| rw_assert (!alloc.newCalled (), 0, __LINE__, |
| "operator new called by operator new(size_t, nothrow)"); |
| |
| #endif // defined _RWSTD_NO_OPERATOR_NEW_NOTHROW |
| |
| #if defined (_RWSTD_NO_OPERATOR_DELETE_NOTHROW) && \ |
| !defined (_RWSTD_NO_PLACEMENT_DELETE) |
| |
| // verify that the nothrow version of operator delete provided |
| // by our library prevents exceptions from propagating |
| TEST_OP_THROWS ((operator delete (0, std::nothrow)), true, |
| "operator delete(void*, nothrow_t) doesn't throw"); |
| |
| // verify that the nothrow version of operator delete provided |
| // by our library doesn't call the ordinary operator delete |
| // (if it did it might cause recursion if a replacement operator |
| // delete were to be implemented in terms of the nothrow version) |
| rw_assert (!alloc.deleteCalled (), 0, __LINE__, |
| "operator delete(void*) called by " |
| "operator delete(void*, nothrow_t)"); |
| |
| #endif // _RWSTD_NO_OPERATOR_DELETE_NOTHROW && _RWSTD_NO_PLACEMENT_DELETE |
| |
| #ifdef _RWSTD_NO_OPERATOR_NEW_ARRAY |
| |
| // verify that the array form of operator new throws bad_alloc on |
| // failure |
| TEST_OP_THROWS ((operator new[] (1)), false, |
| "operator new[] (size_t) throws bad_alloc"); |
| |
| // verify that the array form of operator new provided by our |
| // library calls the ordinary operator new |
| rw_assert (alloc.newCalled (), 0, __LINE__, |
| "operator new called by operator new[](size_t)"); |
| |
| #endif // defined _RWSTD_NO_OPERATOR_NEW_ARRAY |
| |
| #ifdef _RWSTD_NO_OPERATOR_NEW_ARRAY_NOTHROW |
| |
| TEST_OP_THROWS ((ptr = operator new[](1, std::nothrow)), |
| (ptr == 0), |
| "operator new[] (size_t, nothrow_t) returns 0"); |
| |
| rw_assert (alloc.newCalled (), 0, __LINE__, |
| "operator new(size_t) called by " |
| "operator new[](size_t, nothrow_t)"); |
| |
| #endif // defined _RWSTD_NO_OPERATOR_NEW_ARRAY_NOTHROW |
| |
| #ifdef _RWSTD_NO_OPERATOR_DELETE_ARRAY |
| |
| TEST_OP_THROWS ((operator delete[] (0)), true, |
| "operator delete[] (void*) doesn't throw"); |
| |
| rw_assert (alloc.deleteCalled (), 0, __LINE__, |
| "operator delete called by operator delete[](void*)"); |
| |
| #endif // defined _RWSTD_NO_OPERATOR_DELETE_ARRAY |
| |
| #if defined (_RWSTD_NO_OPERATOR_DELETE_ARRAY_NOTHROW) && \ |
| !defined ( _RWSTD_NO_PLACEMENT_DELETE) |
| |
| TEST_OP_THROWS ((operator delete[] (0, std::nothrow)), true, |
| "operator delete[] (void*, nothrow_t) doesn't " |
| "throw"); |
| |
| rw_assert (alloc.deleteCalled (), 0, __LINE__, |
| "operator delete(void*) called by " |
| "operator delete[](void*, nothrow_t)"); |
| |
| #endif // _RWSTD_NO_OPERATOR_DELETE_ARRAY_NOTHROW && |
| // !_RWSTD_NO_PLACEMENT_DELETE |
| |
| // verify that the nothrow form of operator new provided by our |
| // library isn't recursively implemented in terms of the ordinary |
| // operator new |
| rw_assert (0 == recursive_new_nothrow, 0, __LINE__, |
| "operator new (size_t, nothrow_t) causes recursion"); |
| |
| rw_assert (0 == recursive_delete_nothrow, 0, __LINE__, |
| "operator delete (void*, nothrow_t) causes recursion"); |
| } |
| |
| // __rw_allocate() is like ::operator new() except that it calls |
| // __rw_throw() on failure (which may throw std::bad_alloc). |
| // This test checks that __rw_throw() is called, |
| // that it properly calls __rw_throw_proc(), and that bad_alloc |
| // is thrown. We'll use the replaced ::operator new() to |
| // control when a memory allocation request will fail. |
| |
| #ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE |
| |
| const bool test_rw_allocate = true; |
| |
| #else // if defined (_RWSTD_NO_REPLACEABLE_NEW_DELETE) |
| |
| // avoid tests that depend on the replacement operators new |
| // and delete on platforms like AIX or Win32 where they cannot |
| // be reliably replaced |
| const bool test_rw_allocate = false; |
| |
| #endif // _RWSTD_NO_REPLACEABLE_NEW_DELETE |
| |
| if (test_rw_allocate) { |
| |
| alloc.init (); // reset the allocator |
| |
| _RW::__rw_throw_proc = my_throw_proc; // set the throw func |
| my_throw_proc_called = false; |
| |
| for (std::size_t i = 0; i < 10; ++i) { |
| |
| char* ptr = 0; |
| |
| const bool doFail = !!(i % 2); // prevent MSVC warning 4800 |
| if (doFail) |
| alloc._failAlloc = true; |
| |
| try { |
| // allocation |
| alloc._bytesRequested = ~i; // reset |
| |
| ptr = _RWSTD_STATIC_CAST (char*, _RW::__rw_allocate (i)); |
| rw_assert (alloc._bytesRequested == i, 0, __LINE__, |
| "__rw_allocate (%u) requested %u bytes", |
| i, alloc._bytesRequested); |
| |
| rw_assert (alloc.newCalled (), 0, __LINE__, |
| "operator new() called"); |
| rw_assert (!doFail, 0, __LINE__, |
| "allocation succeeded"); |
| rw_assert (my_throw_proc_called == false, |
| 0, __LINE__, |
| "my_throw_proc called"); |
| |
| // deallocation |
| _RW::__rw_deallocate (ptr, i); |
| |
| rw_assert (alloc.deleteCalled (), |
| 0, __LINE__, |
| "operator delete() called"); |
| rw_assert (alloc._deallocPtr == ptr, |
| 0, __LINE__, |
| "allocated memory deallocated"); |
| |
| } |
| catch (std::bad_alloc) { |
| rw_assert (doFail, 0, __LINE__, |
| "expected bad_alloc exception"); |
| |
| rw_assert (my_throw_proc_called == true, 0, __LINE__, |
| "my_throw_proc called"); |
| } |
| catch (...) { |
| rw_assert (false, 0, __LINE__, |
| "expected std::bad_alloc exception"); |
| } |
| |
| my_throw_proc_called = false; |
| } |
| } |
| else { |
| rw_warn (false, 0, __LINE__, |
| "__rw::__rw_allocate() not exercised: " |
| "_RWSTD_NO_REPLACEABLE_NEW_DELETE #defined"); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************/ |
| |
| |
| int main (int argc, char* argv []) |
| { |
| return rw_test (argc, argv, __FILE__, |
| "lib.support.dynamic", |
| 0 /* no comment */, |
| run_test, |
| "", |
| (void*)0); |
| } |