Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
18.support.dynamic.cpp
Go to the documentation of this file.
1
29#include <new>
30#include <memory>
31
32#include <rw_driver.h>
33
34#include <rw/_defs.h>
35#include <rw/_error.h>
36
37#include <cstdlib> // for malloc() and free()
38#include <cstring> // for strlen()
39
40
41#ifdef _MSC_VER
42 // verify that <new> et al can coexist with MSVC's <new.h>
43# include <new.h>
44#endif
45
46
48// detects recursive implementation of operator new (size_t, nothrow_t)
49static int recursive_new_nothrow = 0;
50static int recursive_delete_nothrow = 0;
51
52// MemoryAllocator is used to replace the global new and delete
53// in order to test conformance of of the other operators that
54// might be defined in <new>
55
57{
59 init ();
60 }
61
62 void* operatorNew (std::size_t n) _THROWS ((std::bad_alloc)) {
63 _allocPtr = 0;
64 _newCalled = true;
66 if (_failAlloc) {
67 _failAlloc = false;
69 }
70#if !defined (_RWSTD_NO_EXT_OPERATOR_NEW) && \
71 defined (_RWSTD_NO_OPERATOR_NEW_NOTHROW)
72
73 // test our implementation of
74 // ::operator new(size_t, const std::nothrow)
75 // this operator must not be implemented in terms
76 // of ::operator new(size_t) to prevent recursion
77
78 if (recursive_new_nothrow++)
79 _allocPtr = std::malloc (_bytesRequested);
80 else
81 _allocPtr = ::operator new (_bytesRequested, std::nothrow);
82
83 --recursive_new_nothrow;
84
85#else
86 _allocPtr = std::malloc (_bytesRequested);
87#endif
88 return _allocPtr;
89 }
90
91 void operatorDelete (void* ptr) _THROWS (()) {
93#if !defined (_RWSTD_NO_EXT_OPERATOR_NEW) && \
94 defined (_RWSTD_NO_OPERATOR_NEW_NOTHROW)
95 // memory allocated by our nothrow operator new()
96 if (recursive_delete_nothrow++ || recursive_new_nothrow)
97 std::free (ptr);
98 else
99 ::operator delete (ptr, std::nothrow);
100
101 --recursive_delete_nothrow;
102#else
103 std::free (ptr);
104#endif
105 _deleteCalled = true;
106 }
107
108 void init () {
110 _bytesRequested = 0;
112 }
113
114 bool newCalled () {
115 bool tmp = _newCalled;
116 _newCalled = 0;
117 return tmp;
118 }
119
120 bool deleteCalled () {
121 bool tmp = _deleteCalled;
122 _deleteCalled = 0;
123 return tmp;
124 }
125
126 std::size_t _bytesRequested;
132};
133
134
135// static instance of memory allocator
137
138
141// replace the global operators with MemoryAllocator versions
142void* operator new (std::size_t size) _THROWS ((std::bad_alloc))
143{
144 return alloc.operatorNew (size);
145}
146
147void operator delete (void* p) _THROWS (())
148{
149 alloc.operatorDelete (p);
150}
151
152
155#define TEST_OP_THROWS(expr,cond,tag) \
156 _TRY { \
157 alloc._failAlloc = true; \
158 expr; \
159 rw_assert (cond, 0, __LINE__, tag); \
160 } \
161 _CATCH (std::bad_alloc) { \
162 RW_ASSERT (!cond, 0, __LINE__, tag); \
163 } \
164 _CATCH (...) { \
165 rw_assert (!cond, 0, __LINE__, tag); \
166 } \
167 (void)0
168
169
172// provide a handler for unexpected exceptions so that at least the
173// test will log the event rather than just aborting
175{
176 rw_assert (false, 0, __LINE__, "caught an unexpected exception\n");
177}
178
179// used in exists_set_new_handler below
181{
182}
183
184// grab default throw procedure
185void (*default_throw_proc)(int, char*) = _RW::__rw_throw_proc;
186
188
189// my_throw_proc just sets a global flag to indicate
190// that it was called, and then dispatches the call
191// to the default throw procedure
192static void my_throw_proc (int id, char* what)
193{
195 default_throw_proc (id, what);
196}
197
198
201static
202int run_test (int, char* [])
203{
205
206 if (1) {
207 // exists_std_nothrow
208 const std::nothrow_t* p = &std::nothrow;
209 rw_assert (p != 0, 0, __LINE__, "std::nothrow defined");
210 }
211
212 if (1) {
213 // exists_bad_alloc
214 std::bad_alloc ba1; // default ctor
215 std::bad_alloc ba2 = ba1; // copy ctor
216
217 std::exception* e = &ba1; // derived from exception
218 (void) &e;
219 ba1 = ba2; // assignment operator
220
221 // verify that a member function is accessible and has the
222 // appropriate signature, including return type and exception
223 // specification
224
225 std::bad_alloc& (std::bad_alloc::*p_op_assign)
226 (const std::bad_alloc&) _PTR_THROWS (())
227 = &std::bad_alloc::operator=;
228 _RWSTD_UNUSED (p_op_assign);
229
230 const char*
231 (std::bad_alloc::*p_what_fn)() const _PTR_THROWS (())
232 = &std::bad_alloc::what;
233 _RWSTD_UNUSED (p_what_fn);
234
235 const char* s = ba1.what ();
236 rw_assert (s && 0 != std::strlen (s), 0, __LINE__,
237 "std::bad_alloc::what() returned bad string");
238 }
239
240 if (1) {
241 // set_new_handler
242 typedef void (*new_handler_t) ();
243
244 new_handler_t (*f) (new_handler_t) _PTR_THROWS (());
246
247 rw_assert (f != 0, 0, __LINE__,
248 "std::set_new_handler() defined");
249
250 new_handler_t original_handler
252
253#if !defined (__EDG__) || __EDG_VERSION__ > 245 || defined (__DECCXX)
254
255 // EDG eccp 2.45 standalone demo is known to fail
256 rw_assert (original_handler == 0, 0, __LINE__,
257 "std::set_new_handler() unexpectedly returned "
258 "installed handler");
259
260#define _RWSTD_NO_EXT_OPERATOR_NEW
261
262#endif // !__EDG__ || __EDG_VERSION__ > 245 || __DECCXX
263
264 new_handler_t replacement_handler
265 = std::set_new_handler (original_handler);
266 rw_assert (my_new_handler == replacement_handler, 0, __LINE__,
267 "std::set_new_handler() failed to return previously "
268 "installed handler");
269 }
270
271 // test all operators that we have provided definitions for
272 // in <new>; exercises the operators by setting the
273 // replacement allocator to fail, and asserting that an
274 // exception is thrown or not as appropriate for the operator
275 if (1) {
276 // operator new throws
277 void* ptr = 0;
279
280 alloc.init (); // reset the allocator
281
282#ifdef _RWSTD_NO_OPERATOR_NEW_NOTHROW
283
284 // verify that the nothrow version of operator new provided
285 // by our library doesn't call the ordinary operator new
286 // (if it did it would cause recursion if a replacement operator
287 // new were to be implemented in terms of the nothrow version)
288
289 // also verify that it returns 0 on failure
290
291 // force a failure by requesting too large a block of storage
292 // size_t (~0) alone isn't good enough since it causes annoying
293 // dialog boxes to be popped up by the MSVCRTD, the MSVC runtime
294 TEST_OP_THROWS ((ptr = operator new (~0U - 4096U, std::nothrow)),
295 (ptr == 0),
296 "operator new(size_t, nothrow_t) returns 0");
297
298 rw_assert (!alloc.newCalled (), 0, __LINE__,
299 "operator new called by operator new(size_t, nothrow)");
300
301#endif // defined _RWSTD_NO_OPERATOR_NEW_NOTHROW
302
303#if defined (_RWSTD_NO_OPERATOR_DELETE_NOTHROW) && \
304 !defined (_RWSTD_NO_PLACEMENT_DELETE)
305
306 // verify that the nothrow version of operator delete provided
307 // by our library prevents exceptions from propagating
308 TEST_OP_THROWS ((operator delete (0, std::nothrow)), true,
309 "operator delete(void*, nothrow_t) doesn't throw");
310
311 // verify that the nothrow version of operator delete provided
312 // by our library doesn't call the ordinary operator delete
313 // (if it did it might cause recursion if a replacement operator
314 // delete were to be implemented in terms of the nothrow version)
315 rw_assert (!alloc.deleteCalled (), 0, __LINE__,
316 "operator delete(void*) called by "
317 "operator delete(void*, nothrow_t)");
318
319#endif // _RWSTD_NO_OPERATOR_DELETE_NOTHROW && _RWSTD_NO_PLACEMENT_DELETE
320
321#ifdef _RWSTD_NO_OPERATOR_NEW_ARRAY
322
323 // verify that the array form of operator new throws bad_alloc on
324 // failure
325 TEST_OP_THROWS ((operator new[] (1)), false,
326 "operator new[] (size_t) throws bad_alloc");
327
328 // verify that the array form of operator new provided by our
329 // library calls the ordinary operator new
330 rw_assert (alloc.newCalled (), 0, __LINE__,
331 "operator new called by operator new[](size_t)");
332
333#endif // defined _RWSTD_NO_OPERATOR_NEW_ARRAY
334
335#ifdef _RWSTD_NO_OPERATOR_NEW_ARRAY_NOTHROW
336
337 TEST_OP_THROWS ((ptr = operator new[](1, std::nothrow)),
338 (ptr == 0),
339 "operator new[] (size_t, nothrow_t) returns 0");
340
341 rw_assert (alloc.newCalled (), 0, __LINE__,
342 "operator new(size_t) called by "
343 "operator new[](size_t, nothrow_t)");
344
345#endif // defined _RWSTD_NO_OPERATOR_NEW_ARRAY_NOTHROW
346
347#ifdef _RWSTD_NO_OPERATOR_DELETE_ARRAY
348
349 TEST_OP_THROWS ((operator delete[] (0)), true,
350 "operator delete[] (void*) doesn't throw");
351
352 rw_assert (alloc.deleteCalled (), 0, __LINE__,
353 "operator delete called by operator delete[](void*)");
354
355#endif // defined _RWSTD_NO_OPERATOR_DELETE_ARRAY
356
357#if defined (_RWSTD_NO_OPERATOR_DELETE_ARRAY_NOTHROW) && \
358 !defined ( _RWSTD_NO_PLACEMENT_DELETE)
359
360 TEST_OP_THROWS ((operator delete[] (0, std::nothrow)), true,
361 "operator delete[] (void*, nothrow_t) doesn't "
362 "throw");
363
364 rw_assert (alloc.deleteCalled (), 0, __LINE__,
365 "operator delete(void*) called by "
366 "operator delete[](void*, nothrow_t)");
367
368#endif // _RWSTD_NO_OPERATOR_DELETE_ARRAY_NOTHROW &&
369 // !_RWSTD_NO_PLACEMENT_DELETE
370
371 // verify that the nothrow form of operator new provided by our
372 // library isn't recursively implemented in terms of the ordinary
373 // operator new
374 rw_assert (0 == recursive_new_nothrow, 0, __LINE__,
375 "operator new (size_t, nothrow_t) causes recursion");
376
377 rw_assert (0 == recursive_delete_nothrow, 0, __LINE__,
378 "operator delete (void*, nothrow_t) causes recursion");
379 }
380
381 // __rw_allocate() is like ::operator new() except that it calls
382 // __rw_throw() on failure (which may throw std::bad_alloc).
383 // This test checks that __rw_throw() is called,
384 // that it properly calls __rw_throw_proc(), and that bad_alloc
385 // is thrown. We'll use the replaced ::operator new() to
386 // control when a memory allocation request will fail.
387
388#ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE
389
390 const bool test_rw_allocate = true;
391
392#else // if defined (_RWSTD_NO_REPLACEABLE_NEW_DELETE)
393
394 // avoid tests that depend on the replacement operators new
395 // and delete on platforms like AIX or Win32 where they cannot
396 // be reliably replaced
397 const bool test_rw_allocate = false;
398
399#endif // _RWSTD_NO_REPLACEABLE_NEW_DELETE
400
401 if (test_rw_allocate) {
402
403 alloc.init (); // reset the allocator
404
405 _RW::__rw_throw_proc = my_throw_proc; // set the throw func
406 my_throw_proc_called = false;
407
408 for (std::size_t i = 0; i < 10; ++i) {
409
410 char* ptr = 0;
411
412 const bool doFail = !!(i % 2); // prevent MSVC warning 4800
413 if (doFail)
414 alloc._failAlloc = true;
415
416 try {
417 // allocation
418 alloc._bytesRequested = ~i; // reset
419
420 ptr = _RWSTD_STATIC_CAST (char*, _RW::__rw_allocate (i));
421 rw_assert (alloc._bytesRequested == i, 0, __LINE__,
422 "__rw_allocate (%u) requested %u bytes",
423 i, alloc._bytesRequested);
424
425 rw_assert (alloc.newCalled (), 0, __LINE__,
426 "operator new() called");
427 rw_assert (!doFail, 0, __LINE__,
428 "allocation succeeded");
430 0, __LINE__,
431 "my_throw_proc called");
432
433 // deallocation
434 _RW::__rw_deallocate (ptr, i);
435
436 rw_assert (alloc.deleteCalled (),
437 0, __LINE__,
438 "operator delete() called");
439 rw_assert (alloc._deallocPtr == ptr,
440 0, __LINE__,
441 "allocated memory deallocated");
442
443 }
444 catch (std::bad_alloc) {
445 rw_assert (doFail, 0, __LINE__,
446 "expected bad_alloc exception");
447
448 rw_assert (my_throw_proc_called == true, 0, __LINE__,
449 "my_throw_proc called");
450 }
451 catch (...) {
452 rw_assert (false, 0, __LINE__,
453 "expected std::bad_alloc exception");
454 }
455
456 my_throw_proc_called = false;
457 }
458 }
459 else {
460 rw_warn (false, 0, __LINE__,
461 "__rw::__rw_allocate() not exercised: "
462 "_RWSTD_NO_REPLACEABLE_NEW_DELETE #defined");
463 }
464
465 return 0;
466}
467
468
471int main (int argc, char* argv [])
472{
473 return rw_test (argc, argv, __FILE__,
474 "lib.support.dynamic",
475 0 /* no comment */,
476 run_test,
477 "",
478 (void*)0);
479}
#define i
Definition 17.names.cpp:126
#define e
Definition 17.names.cpp:118
#define alloc
Definition 17.names.cpp:441
#define ptr
Definition 17.names.cpp:525
#define f
Definition 17.names.cpp:119
#define tmp
Definition 17.names.cpp:553
#define s
#define n
Definition 17.names.cpp:130
#define p
Definition 17.names.cpp:132
#define U
Definition 17.names.cpp:164
void(* default_throw_proc)(int, char *)
void my_unexpected_handler()
bool my_throw_proc_called
void my_new_handler()
#define TEST_OP_THROWS(expr, cond, tag)
#define _PTR_THROWS(spec)
void run_test(const ForwardIterator &dummy, T *, Predicate *pred)
#define main
#define _THROWS()
#define _RWSTD_UNUSED(arg)
Definition _defs.h:972
#define _THROW(ex)
Definition _defs.h:852
#define _RWSTD_STATIC_CAST(T, x)
Definition _defs.h:534
Definition json.h:5363
new_handler set_new_handler(new_handler)
unexpected_handler set_unexpected(void(*)())
const nothrow_t nothrow
_TEST_EXPORT int rw_test(int argc, char *argv[], const char *filename, const char *clause, const char *comment, int(*testfun)(int, char **), const char *optspec,...)
_TEST_EXPORT int rw_assert(int, const char *, int, const char *,...)
Definition driver.cpp:1630
_TEST_EXPORT int rw_warn(int, const char *, int, const char *,...)
Definition driver.cpp:1647
void * operatorNew(std::size_t n) _THROWS((std
void operatorDelete(void *ptr) _THROWS(())