Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
21.string.swap.cpp
Go to the documentation of this file.
1
29#include <string> // for string
30#include <cstddef> // size_t
31#include <exception> // for exception
32
33#include <rw_strings.h> // for StringIds
34#include <rw_driver.h> // for rw_assert()
35#include <rw_allocator.h> // foir UserAlloc
36#include <rw_char.h> // for rw_expand()
37#include <rw_new.h> // for bad_alloc, replacement operator new
38
39
41// for convenience and brevity
42#define Swap(sig) StringIds::swap_ ## sig
43
44static const char* const exceptions[] = {
45 "unknown exception", "out_of_range", "length_error",
46 "bad_alloc", "exception"
47};
48
49
51// exercises:
52// swap (basic_string&)
53static const StringTestCase
54str_test_cases [] = {
55
56#undef TEST
57#define TEST(str, arg) \
58 { __LINE__, -1, -1, -1, -1, -1, \
59 str, sizeof str - 1, arg, sizeof arg - 1, \
60 0, 0, 0 \
61 }
62
63 // +------------------------- controlled "destination" (str) sequence
64 // | +-------- controlled "source" (arg) sequence
65 // | |
66 // V V
67 TEST ("", ""),
68 TEST ("", "a"),
69 TEST ("a", ""),
70 TEST ("", "<U0>"),
71 TEST ("<U0>", ""),
72
73 TEST ("a", "b"),
74 TEST ("a", "bc"),
75 TEST ("ab", "c"),
76
77 TEST ("a<U0>b<U0>@2c", "<U0>b<U0>@2c"),
78 TEST ("<U0>b<U0>@2c", "a<U0>b<U0>@2c"),
79
80 TEST ("a<U0>b<U0>@2c", "<U0>@2"),
81 TEST ("<U0>@2", "a<U0>b<U0>@2c"),
82
83 TEST ("x@4096", ""),
84 TEST ("", "x@4096"),
85 TEST ("x@4096", "<U0>@3"),
86 TEST ("<U0>@3", "x@4096"),
87 TEST ("x@4096", "x@4096"),
88
89 TEST ("", "x@128"),
90 TEST ("x@207", "x@128"),
91 TEST ("x@128", "x@334"),
92 TEST ("x@873", "x@334"),
93 TEST ("x@1412", "x@540"),
94 TEST ("x@540", "x@2284"),
95 TEST ("x@3695", "x@2284"),
96 TEST ("x@3695", "x@128"),
97
98 TEST ("", 0),
99 TEST ("<U0>", 0),
100 TEST ("abc", 0),
101 TEST ("a<U0>b<U0>@2c", 0),
102 TEST ("x@4096", 0),
103
104 TEST (0, ""),
105 TEST (0, "<U0>"),
106 TEST (0, "abc@1024"),
107 TEST (0, "a<U0>b<U0>@2c"),
108 TEST (0, "x@4096"),
109
110 TEST ("last", "test")
111};
112
113
114
116template <class charT, class Traits, class Allocator>
118 Allocator &a1,
119 Allocator &a2,
120 const StringTestCaseData<charT> &tdata)
121{
122 typedef std::basic_string <charT, Traits, Allocator> String;
123 typedef UserTraits<UserChar>::MemFun UTMemFun;
124
125 const StringTestCase &tcase = tdata.tcase_;
126
127 // construct the string object to be modified
128 // and the argument string
129 String s_str (tdata.str_, tdata.strlen_, a1);
130 String s_arg (tdata.arg_, tdata.arglen_, a2);
131
132 const charT* const p1 = s_str.data ();
133 const charT* const p2 = tcase.arg ? s_arg.data () : s_str.data ();
134
135 const char* const src = tcase.arg ? tcase.arg : tcase.str;
136 const std::size_t src_len = tcase.arg ? tcase.arg_len : tcase.str_len;
137 const std::size_t srclen_ = tcase.arg ? tdata.arglen_ : tdata.strlen_;
138 String& arg_str = tcase.arg ? s_arg : s_str;
139
140 // save the state of the string object before the call
141 // to detect wxception safety violations (changes to
142 // the state of the object after an exception)
143 const StringState str_state (rw_get_string_state (s_str));
144 const StringState arg_str_state (rw_get_string_state (arg_str));
145
146 std::size_t n_total_op_assign = 0;
147 std::size_t n_total_op_assign2 = 0;
148 std::size_t n_total_op_copy = 0;
149 std::size_t n_total_op_move = 0;
150
151 std::size_t* const rg_calls = rw_get_call_counters ((Traits*)0, (charT*)0);
152 if (rg_calls) {
153 n_total_op_assign = rg_calls[UTMemFun::assign];
154 n_total_op_assign2 = rg_calls[UTMemFun::assign2];
155 n_total_op_copy = rg_calls[UTMemFun::copy];
156 n_total_op_move = rg_calls[UTMemFun::move];
157 }
158
159 rwt_free_store* const pst = rwt_get_free_store (0);
160 SharedAlloc* const pal = SharedAlloc::instance ();
161
162 // iterate for`throw_count' starting at the next call to operator new,
163 // forcing each call to throw an exception, until the function finally
164 // succeeds (i.e, no exception is thrown)
165 std::size_t throw_count;
166 for (throw_count = 0; ; ++throw_count) {
167
168 const char* expected = 0;
169 const char* caught = 0;
170
171#ifndef _RWSTD_NO_EXCEPTIONS
172
173 // no exceptions expected
174 if (0 == tcase.bthrow) {
175 // by default excercise the exception safety of the function
176 // by iteratively inducing an exception at each call to operator
177 // new or Allocator::allocate() until the call succeeds
178 expected = exceptions [3]; // bad_alloc
179 *pst->throw_at_calls_ [0] = pst->new_calls_ [0] + throw_count + 1;
180 pal->throw_at_calls_ [pal->m_allocate] =
181 pal->throw_at_calls_ [pal->m_allocate] + throw_count + 1;
182 }
183
184#else // if defined (_RWSTD_NO_EXCEPTIONS)
185
186 if (tcase.bthrow)
187 return;
188
189#endif // _RWSTD_NO_EXCEPTIONS
190
191 try {
192
193 // start checking for memory leaks
194 rw_check_leaks (s_str.get_allocator ());
195 rw_check_leaks (arg_str.get_allocator ());
196
197 if (0 == tcase.str)
198 String ().swap (arg_str);
199 else
200 s_str.swap (arg_str);
201
202 if (rg_calls) {
203
204 std::size_t n_op_assign =
205 rg_calls[UTMemFun::assign] - n_total_op_assign;
206 std::size_t n_op_assign2 =
207 rg_calls[UTMemFun::assign2] - n_total_op_assign2;
208 std::size_t n_op_copy =
209 rg_calls[UTMemFun::copy] - n_total_op_copy;
210 std::size_t n_op_move =
211 rg_calls[UTMemFun::move] - n_total_op_move;
212
213 bool success =
214 0 == (n_op_assign | n_op_assign2 | n_op_copy | n_op_move);
215
216 rw_assert (success, 0, tcase.line,
217 "line %d. %{$FUNCALL}: complexity: %zu assigns, "
218 "%zu assign2s, %zu copies, %zu moves", __LINE__,
219 n_op_assign, n_op_assign2, n_op_copy, n_op_move);
220 }
221
222 if (0 == tcase.str) {
223
224 rw_assert (0 == arg_str.capacity (), 0, tcase.line,
225 "line %d. %{$FUNCALL}: expected 0 capacity, "
226 "got %zu", __LINE__, arg_str.capacity ());
227 }
228 else {
229
230 const charT* const res_p1 = s_str.data ();
231 const charT* const res_p2 =
232 tcase.arg ? s_arg.data () : s_str.data ();
233
234 const std::size_t res1_len = s_str.size ();
235 const std::size_t res2_len =
236 tcase.arg ? s_arg.size () : s_str.size ();
237
238 rw_assert (res_p1 == p2 && res_p2 == p1, 0, tcase.line,
239 "line %d. %{$FUNCALL}: got offset %td from "
240 "expected value, arg.data (): got offset %td "
241 "from expected value",
242 __LINE__, p2 - res_p1, p1 - res_p2);
243
244 std::size_t match =
245 rw_match (tcase.str, res_p2, res2_len);
246
247 rw_assert (match == tdata.strlen_, 0, tcase.line,
248 "line %d. %{$FUNCALL}: this == %{#*s}, got this = "
249 "%{/*.*Gs}, differs at pos %zu",
250 __LINE__, int (src_len), src, int (sizeof (charT)),
251 int (res1_len), res_p1, match);
252
253 match = rw_match (src, res_p1, res1_len);
254
255 rw_assert (match == srclen_, 0, tcase.line,
256 "line %d. %{$FUNCALL}: str == %{#*s}, got str = "
257 "%{/*.*Gs}, differs at pos %zu",
258 __LINE__, int (tcase.str_len), tcase.str,
259 int (sizeof (charT)), int (res2_len),
260 res_p2, match);
261 }
262 }
263
264#ifndef _RWSTD_NO_EXCEPTIONS
265
266 catch (const std::exception &ex) {
267 caught = exceptions [4];
268 rw_assert (0, 0, tcase.line,
269 "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
270 "unexpectedly%{;} caught std::%s(%#s)",
271 __LINE__, 0 != expected, expected, caught, ex.what ());
272 }
273 catch (...) {
274 caught = exceptions [0];
275 rw_assert (0, 0, tcase.line,
276 "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
277 "unexpectedly%{;} caught %s",
278 __LINE__, 0 != expected, expected, caught);
279 }
280
281#endif // _RWSTD_NO_EXCEPTIONS
282
283 // FIXME: verify the number of blocks the function call
284 // is expected to allocate and detect any memory leaks
285 rw_check_leaks (s_str.get_allocator (), tcase.line,
286 std::size_t (-1), std::size_t (-1));
287
288 rw_check_leaks (arg_str.get_allocator (), tcase.line,
289 std::size_t (-1), std::size_t (-1));
290
291 if (caught) {
292 // verify that an exception thrown during allocation
293 // didn't cause a change in the state of the object
294 str_state.assert_equal (rw_get_string_state (s_str),
295 __LINE__, tcase.line, caught);
296
297 arg_str_state.assert_equal (rw_get_string_state (arg_str),
298 __LINE__, tcase.line, caught);
299
300 if (0 == tcase.bthrow) {
301 // allow this call to operator new to succeed and try
302 // to make the next one to fail during the next call
303 // to the same function again
304 continue;
305 }
306 }
307 else if (0 < tcase.bthrow) {
308 rw_assert (caught == expected, 0, tcase.line,
309 "line %d. %{$FUNCALL} %{?}expected %s, caught %s"
310 "%{:}unexpectedly caught %s%{;}",
311 __LINE__, 0 != expected, expected, caught, caught);
312 }
313
314 break;
315 }
316
317 // no exception expected
318 const std::size_t expect_throws = 0;
319
320 rw_assert (expect_throws == throw_count, 0, tcase.line,
321 "line %d: %{$FUNCALL}: expected exactly 0 %s exception "
322 "while the swap, got %zu",
323 __LINE__, exceptions [3], throw_count);
324
325 // disable bad_alloc exceptions
326 *pst->throw_at_calls_ [0] = 0;
327 pal->throw_at_calls_ [pal->m_allocate] = 0;
328}
329
330
332template <class charT>
333std::allocator<charT>
334make_alloc (SharedAlloc&, std::allocator<charT>*) {
335 return std::allocator<charT>();
336}
337
338template <class charT, class Types>
341 return UserAlloc<charT, Types>(&shal);
342}
343
344
346template <class charT, class Traits, class Allocator>
348 const StringTestCaseData<charT> &tdata)
349{
350 SharedAlloc sa1;
351 Allocator a1 = make_alloc(sa1, (Allocator*)0);
352
353 // test swap using the same allocator objects
354 test_swap ((charT*)0, (Traits*)0, a1, a1, tdata);
355
356 SharedAlloc sa2;
357 Allocator a2 = make_alloc(sa2, (Allocator*)0);
358
359 if (a1 != a2) {
360 // test swap using different allocator objects
361 test_swap ((charT*)0, (Traits*)0, a1, a2, tdata);
362 }
363}
364
365
368
369int main (int argc, char** argv)
370{
371 static const StringTest
372 tests [] = {
373
374#undef TEST
375#define TEST(sig) { \
376 Swap (sig), sig ## _test_cases, \
377 sizeof sig ## _test_cases / sizeof *sig ## _test_cases, \
378 }
379
380 TEST (str)
381 };
382
383 const std::size_t test_count = sizeof tests / sizeof *tests;
384
385 const int status =
386 rw_run_string_test (argc, argv, __FILE__,
387 "lib.string.swap",
388 test_swap_func_array, tests, test_count);
389
390 return status;
391}
#define p2
Definition 17.names.cpp:265
#define a2
Definition 17.names.cpp:251
#define a1
Definition 17.names.cpp:225
#define charT
Definition 17.names.cpp:456
#define p1
Definition 17.names.cpp:239
#define TEST(str, arg)
std::allocator< charT > make_alloc(SharedAlloc &, std::allocator< charT > *)
void test_swap()
std::char_traits< S > Traits
#define main
int caught
const StringTestCase & tcase_
Definition rw_strings.h:692
const charT *const arg_
Definition rw_strings.h:688
const charT *const str_
Definition rw_strings.h:687
const char * str
Definition exec.cpp:170
_TEST_EXPORT void rw_check_leaks(const SharedAlloc *=0, int=0, _RWSTD_SIZE_T=0, _RWSTD_SIZE_T=_RWSTD_SIZE_MAX)
_RWSTD_SIZE_T * rw_get_call_counters(std::char_traits< charT > *, charT *)
Definition rw_char.h:495
_TEST_EXPORT _RWSTD_SIZE_T rw_match(const char *, const char *, _RWSTD_SIZE_T=_RWSTD_SIZE_MAX)
_TEST_EXPORT int rw_assert(int, const char *, int, const char *,...)
Definition driver.cpp:1630
_TEST_EXPORT rwt_free_store * rwt_get_free_store(rwt_free_store *)
Definition new.cpp:597
StringState rw_get_string_state(const String &str)
Definition rw_strings.h:752
_TEST_EXPORT int rw_run_string_test(int, char **, const char *, const char *, StringTestFunc *, const StringTest *, _RWSTD_SIZE_T)
#define DEFINE_STRING_TEST_FUNCTIONS(fname)
Definition rw_strings.h:909
static SharedAlloc * instance(SharedAlloc *=0)
_RWSTD_SIZE_T throw_at_calls_[n_funs]
int assert_equal(const StringState &, int, int, const char *) const
_RWSTD_SIZE_T arg_len
Definition rw_strings.h:600
_RWSTD_SIZE_T str_len
Definition rw_strings.h:597
const char * str
Definition rw_strings.h:596
const char * arg
Definition rw_strings.h:599
_RWSTD_SIZE_T * throw_at_calls_[2]
Definition rw_new.h:83
_RWSTD_SIZE_T new_calls_[2]
Definition rw_new.h:47