| /************************************************************************ |
| * |
| * 23.containers.cpp - definitions of helpers used in clause 23 tests |
| * |
| * $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. |
| * |
| **************************************************************************/ |
| |
| // expand _TEST_EXPORT macros |
| #define _RWSTD_TEST_SRC |
| |
| #include <memory> // for allocator |
| |
| #include <rw_containers.h> |
| #include <rw_lists.h> |
| #include <rw_cmdopt.h> // for rw_enabled() |
| #include <rw_driver.h> // for rw_note() |
| #include <rw_allocator.h> // for UserAlloc |
| #include <rw_printf.h> // for rw_asnprintf() |
| |
| #include <ctype.h> // for isdigit() |
| #include <stdarg.h> // for va_arg, ... |
| #include <stddef.h> // for size_t |
| #include <stdlib.h> // for free() |
| #include <string.h> // for strcpy() |
| |
| /**************************************************************************/ |
| |
| static const char |
| _rw_this_file[] = __FILE__; |
| |
| |
| static const char* const |
| _rw_elem_names[] = { |
| "UserPOD", "UserClass" |
| }; |
| |
| |
| static const char* const |
| _rw_alloc_names[] = { |
| "allocator", "UserAlloc" |
| }; |
| |
| |
| static const char* const |
| _rw_iter_names[] = { |
| "", |
| "InputIterator", "ForwardIterator", "BidirectionalIterator", |
| "RandomAccessIterator", |
| "pointer", "const_pointer", |
| "iterator", "const_iterator", |
| "reverse_iterator", "const_reverse_iterator" |
| }; |
| |
| |
| static const char* const |
| _rw_cont_names[] = { |
| "list", "vector", "deque", "queue", "stack" |
| }; |
| |
| |
| // order of elements depends on the values of ContainerIds::FuncId |
| static const char* const |
| _rw_func_names[] = { |
| 0 /* special handling for the ctor */, "operator=", "assign", |
| "get_allocator", "begin", "end", "rbegin", "rend", "empty", |
| "size", "max_size", "resize", "insert", "erase", "swap", |
| "clear", "operator==", "operator<", "operator!=", "operator>", |
| "operator>=", "operator<=", "push_back", "front", "back", |
| "pop_back", "push_front", "pop_front", "splice", "remove", |
| "remove_if", "unique", "merge", "sort", "reverse", "operator[]", |
| "at", "capacity", "reserve", "flip" |
| }; |
| |
| /**************************************************************************/ |
| |
| const size_t MAX_OVERLOADS = 32; |
| |
| // disabled (-1) or explicitly enabled (+1) for each overload |
| // of the cont function being tested |
| static int |
| _rw_opt_func [MAX_OVERLOADS]; |
| |
| // array of tests each exercising a single cont function |
| static const ContainerTest* |
| _rw_cont_tests; |
| |
| // size of the array above |
| static size_t |
| _rw_cont_test_count; |
| |
| static int |
| _rw_opt_elem_types [sizeof _rw_elem_names / sizeof *_rw_elem_names]; |
| |
| static int |
| _rw_opt_alloc_types [sizeof _rw_alloc_names / sizeof *_rw_alloc_names]; |
| |
| static int |
| _rw_opt_iter_types [sizeof _rw_iter_names / sizeof *_rw_iter_names]; |
| |
| static int |
| _rw_opt_no_exceptions; |
| |
| static int |
| _rw_opt_no_exception_safety; |
| |
| static int |
| _rw_opt_self_ref; |
| |
| /**************************************************************************/ |
| |
| static size_t |
| _rw_get_func_inx (size_t fid) |
| { |
| size_t inx = _RWSTD_SIZE_MAX; |
| |
| for (size_t i = 0; _rw_cont_test_count; ++i) { |
| if (fid == _rw_cont_tests [i].which) { |
| inx = i; |
| break; |
| } |
| } |
| |
| RW_ASSERT (inx < _RWSTD_SIZE_MAX); |
| |
| return inx; |
| } |
| |
| /**************************************************************************/ |
| |
| static const char* |
| _rw_class_name (const ContainerFunc & func) |
| { |
| return _rw_cont_names [func.cont_id_]; |
| } |
| |
| /**************************************************************************/ |
| |
| // appends the signature of the function specified by which |
| // to the provided buffer; when the second argument is null, |
| // appends the mnemonic representing the signature, including |
| // the name of the function, as specified by the third argument |
| static void |
| _rw_sigcat (char **pbuf, size_t *pbufsize, |
| const ContainerFunc *func, |
| size_t which = 0) |
| { |
| // for convenience |
| typedef ContainerIds Ids; |
| |
| if (func) |
| which = func->which_; |
| |
| // determine whether the function is a member function |
| const bool is_member = 0 != (Ids::bit_member & which); |
| |
| // get the bitmap describing the function's argument types |
| int argmap = int ((which & ~Ids::bit_member) >> Ids::fid_bits); |
| |
| // determine whether the function is a const member function |
| bool is_const_member = |
| is_member && Ids::arg_ccont == (argmap & Ids::arg_mask); |
| |
| // remove the *this argument if the function is a member |
| if (is_member) |
| argmap >>= Ids::arg_bits; |
| |
| const char* funcname = 0; |
| |
| if (0 == func) { |
| const Ids::FuncId fid = Ids::FuncId (which & ContainerIds::fid_mask); |
| |
| switch (fid) { |
| // translate names with funky characters to mnemonics |
| case Ids::fid_ctor: funcname = "ctor"; break; |
| case Ids::fid_op_index: funcname = "op_index"; break; |
| case Ids::fid_op_set: funcname = "op_assign"; break; |
| case Ids::fid_op_equal: funcname = "op_equal"; break; |
| case Ids::fid_op_less: funcname = "op_less"; break; |
| case Ids::fid_op_not_equal: funcname = "op_not_equal"; break; |
| case Ids::fid_op_greater: funcname = "op_greater"; break; |
| case Ids::fid_op_greater_equal: funcname = "op_greater_equal"; break; |
| case Ids::fid_op_less_equal: funcname = "op_less_equal"; break; |
| |
| case Ids::fid_get_allocator: |
| case Ids::fid_empty: |
| case Ids::fid_size: |
| case Ids::fid_max_size: |
| // prevent appending the "_const" bit to the mnemonics |
| // of member functions not overloaded on const |
| is_const_member = false; |
| |
| // fall through |
| |
| default: { |
| // determine the cont function name (for brief output) |
| const size_t nfuncs = |
| sizeof _rw_func_names / sizeof *_rw_func_names; |
| |
| RW_ASSERT (size_t (fid) < nfuncs); |
| |
| funcname = _rw_func_names [fid]; |
| RW_ASSERT (0 != funcname); |
| break; |
| } |
| } |
| } |
| |
| // iterator name for member templates, empty cont for other functions |
| const char* const iname = func ? _rw_iter_names [func->iter_id_] : ""; |
| |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+}%{?}%s%{?}_const%{;}%{:}%{?}<%s>%{;}(%{;}", |
| 0 == func, funcname, is_const_member, 0 != *iname, iname); |
| |
| char iname_buf [80]; |
| *iname_buf = '\0'; |
| |
| // iterate through the map of argument types one field at a time |
| // determining and formatting the type of each argument until |
| // void is reached |
| for (size_t argno = 0; argmap; ++argno, argmap >>= Ids::arg_bits) { |
| |
| const char* pfx = ""; |
| const char* sfx = ""; |
| |
| const int argtype = argmap & Ids::arg_mask; |
| |
| const char* tname = 0; |
| |
| if (func) { |
| switch (argtype) { |
| case Ids::arg_size: tname = "size_type"; break; |
| case Ids::arg_val: tname = "value_type"; break; |
| case Ids::arg_ref: tname = "reference"; break; |
| case Ids::arg_cref: tname = "const_reference"; break; |
| case Ids::arg_iter: tname = "iterator"; break; |
| case Ids::arg_citer: tname = "const_iterator"; break; |
| case Ids::arg_range: |
| if ('\0' == *iname_buf) { |
| strcpy (iname_buf, iname); |
| strcat (iname_buf, ", "); |
| strcat (iname_buf, iname); |
| } |
| tname = iname_buf; |
| break; |
| |
| case Ids::arg_ccont: |
| pfx = "const "; |
| // fall through |
| case Ids::arg_cont: |
| tname = _rw_class_name (*func); |
| sfx = "&"; |
| break; |
| |
| case Ids::arg_alloc: tname = "const allocator_type&"; break; |
| case Ids::arg_pred: tname = "Predicate"; break; |
| case Ids::arg_bpred: tname = "BinaryPredicate"; break; |
| case Ids::arg_comp: tname = "Compare"; break; |
| } |
| } |
| else { |
| switch (argtype) { |
| case Ids::arg_size: tname = "size"; break; |
| case Ids::arg_val: tname = "val"; break; |
| case Ids::arg_ref: tname = "ref"; break; |
| case Ids::arg_cref: tname = "cref"; break; |
| case Ids::arg_iter: tname = "iter"; break; |
| case Ids::arg_citer: tname = "citer"; break; |
| case Ids::arg_range: tname = "range"; break; |
| case Ids::arg_cont: tname = "cont"; break; |
| case Ids::arg_ccont: tname = "ccont"; break; |
| case Ids::arg_alloc: tname = "alloc"; break; |
| case Ids::arg_pred: tname = "pred"; break; |
| case Ids::arg_bpred: tname = "bpred"; break; |
| case Ids::arg_comp: tname = "comp"; break; |
| } |
| } |
| |
| RW_ASSERT (0 != tname); |
| |
| if ( 0 == func || is_member |
| || Ids::arg_cont != argtype && Ids::arg_ccont != argtype) { |
| // append the name or mnemonic of the argument type |
| rw_asnprintf (pbuf, pbufsize, "%{+}%{?}_%{:}%{?}, %{;}%{;}%s%s%s", |
| 0 == func, 0 < argno, pfx, tname, sfx); |
| } |
| else { |
| // in non-member functions use ${CLASS} to format |
| // the cont argument in order to expand |
| // its template argument cont |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+}%{?}, %{;}%{?}const %{;}%{$CLASS}&", |
| 0 < argno, Ids::arg_ccont == argtype); |
| |
| } |
| } |
| |
| if (func) |
| rw_asnprintf (pbuf, pbufsize, "%{+})%{?} const%{;}", is_const_member); |
| } |
| |
| /**************************************************************************/ |
| |
| // returns the zero-based index of the argument type specified |
| // by arg in the function signature given by which |
| static int |
| _rw_argno (size_t which, int arg) |
| { |
| // get the bitmap describing the function's argument types |
| int argmap = int ((which & ~ContainerIds::bit_member) |
| >> ContainerIds::fid_bits); |
| |
| int argno = 0; |
| |
| // iterate over argument types looking for the first one |
| // that equals arg |
| for (; argmap; argmap >>= ContainerIds::arg_bits, ++argno) { |
| if ((argmap & ContainerIds::arg_mask) == arg) { |
| return argno; |
| } |
| } |
| |
| // return -1 when the function doesn't take an argument |
| // of the specified type |
| return -1; |
| } |
| |
| /**************************************************************************/ |
| |
| static void |
| _rw_list_sigcat (char** pbuf, size_t * pbufsize, ListIds::OverloadId which, |
| bool self, const char* str, size_t str_len, |
| const char* arg, size_t arg_len, |
| const ContainerTestCase &tcase) |
| { |
| // compute the end offsets for convenience |
| const size_t range1_end = tcase.off + tcase.size; |
| const size_t range2_end = tcase.off2 + tcase.size2; |
| |
| // determine whether the function takes an allocator_type argument |
| const bool use_alloc = 0 < _rw_argno (which, ContainerIds::arg_alloc); |
| |
| // format and append cont function arguments abbreviating complex |
| // expressions as much as possible to make them easy to understand |
| switch (which) { |
| case ListIds::ctor_ccont: |
| case ListIds::op_set_ccont: |
| case ListIds::swap_cont: |
| case ListIds::merge_cont: |
| // format self-referential cont argument as *this |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} (%{?}*this%{:}%{$Container}(%{#*s})%{;}" |
| "%{?}, const allocator_type&%{;})", |
| self, int (arg_len), arg, use_alloc); |
| break; |
| |
| case ListIds::assign_size_cref: |
| case ListIds::ctor_size_cref: |
| case ListIds::ctor_size_cref_alloc: |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} (%zu, %{#c}%{?}, const allocator_type&%{;})", |
| tcase.size, tcase.val, use_alloc); |
| break; |
| |
| case ListIds::assign_range: |
| case ListIds::ctor_range: |
| case ListIds::ctor_range_alloc: |
| rw_asnprintf (pbuf, pbufsize, "%{+}<%{$Iterator:-Iterator}>(" |
| "%{?}begin()%{:}%{$Iterator:-Iterator}(%{#*s})%{;}" |
| "%{?} + %zu%{;}, " |
| "%{?}begin()%{:}%{$Iterator:-Iterator}(...)%{;}" |
| "%{?} + %zu%{;}" |
| "%{?}, const allocator_type&%{;})", |
| self, int (arg_len), arg, |
| 0 != tcase.off2, tcase.off2, |
| self, 0 != range2_end, range2_end, use_alloc); |
| break; |
| |
| case ListIds::insert_iter_cref: |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} (begin()%{?} + %zu%{;}, %{#c})", |
| 0 != tcase.off, tcase.off, tcase.val); |
| break; |
| |
| case ListIds::insert_iter_size_cref: |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} (begin()%{?} + %zu%{;}, %zu, %{#c})", |
| 0 != tcase.off, tcase.off, tcase.size, tcase.val); |
| break; |
| |
| case ListIds::insert_iter_range: |
| rw_asnprintf (pbuf, pbufsize, "%{+}<%{$Iterator:-Iterator}>" |
| "(begin()%{?} + %zu%{;}, " |
| "%{?}begin()%{:}%{$Iterator:-Iterator}(%{#*s})%{;}" |
| "%{?} + %zu%{;}, " |
| "%{?}begin()%{:}%{$Iterator:-Iterator}(...)%{;}" |
| "%{?} + %zu%{;})", |
| 0 != tcase.off, tcase.off, |
| self, int (arg_len), arg, |
| 0 != tcase.off2, tcase.off2, |
| self, 0 != range2_end, range2_end); |
| break; |
| |
| case ListIds::push_front_cref: |
| case ListIds::push_back_cref: |
| case ListIds::remove_cref: |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} (%{#c})", tcase.val); |
| break; |
| |
| case ListIds::ctor_void: |
| case ListIds::get_allocator_void: |
| case ListIds::begin_void: |
| case ListIds::end_void: |
| case ListIds::rbegin_void: |
| case ListIds::rend_void: |
| case ListIds::empty_void: |
| case ListIds::size_void: |
| case ListIds::max_size_void: |
| case ListIds::front_void: |
| case ListIds::back_void: |
| case ListIds::pop_front_void: |
| case ListIds::pop_back_void: |
| case ListIds::clear_void: |
| case ListIds::sort_void: |
| case ListIds::reverse_void: |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} ()"); |
| break; |
| |
| case ListIds::begin_const_void: |
| case ListIds::end_const_void: |
| case ListIds::rbegin_const_void: |
| case ListIds::rend_const_void: |
| case ListIds::front_const_void: |
| case ListIds::back_const_void: |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} () const"); |
| break; |
| |
| case ListIds::ctor_alloc: |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} (const allocator_type&)"); |
| break; |
| |
| case ListIds::erase_iter: |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} (begin()%{?} + %zu%{;})", |
| 0 != tcase.off, tcase.off); |
| break; |
| |
| case ListIds::erase_iter_iter: |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} (begin()%{?} + %zu%{;}, begin()%{?} + %zu%{;})", |
| 0 != tcase.off, tcase.off, |
| 0 != range1_end, range1_end); |
| break; |
| |
| case ListIds::op_equal_ccont_ccont: |
| case ListIds::op_less_ccont_ccont: |
| case ListIds::op_not_equal_ccont_ccont: |
| case ListIds::op_greater_ccont_ccont: |
| case ListIds::op_greater_equal_ccont_ccont: |
| case ListIds::op_less_equal_ccont_ccont: |
| // format zero cont argument without size as arg |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} (%{?}arg2%{:}%{$CLASS}(%{#*s})%{;}, " |
| "%{?}arg1%{:}%{$CLASS}(%{#*s})%{;})", |
| 0 == str, int (str_len), str, self, int (arg_len), arg); |
| break; |
| |
| case ListIds::ctor_size: |
| case ListIds::resize_size: |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} (%zu)", tcase.size); |
| break; |
| |
| case ListIds::resize_size_val: |
| rw_asnprintf (pbuf, pbufsize, |
| "%{+} (%zu, %{#c})", tcase.size, tcase.val); |
| break; |
| |
| case ListIds::swap_cont_cont: |
| case ListIds::splice_iter_cont: |
| case ListIds::splice_iter_cont_iter: |
| case ListIds::splice_iter_cont_iter_iter: |
| case ListIds::remove_if_pred: |
| case ListIds::unique_bpred: |
| case ListIds::merge_cont_comp: |
| case ListIds::sort_comp: |
| rw_asnprintf (pbuf, pbufsize, |
| "not yet defined"); |
| break; |
| |
| default: |
| RW_ASSERT (!"test logic error: unknown list overload"); |
| } |
| } |
| |
| /**************************************************************************/ |
| |
| // temporary, should be defined in 23.deque.h |
| struct VectorIds : ContainerIds |
| { |
| enum OverloadId { |
| dummy |
| }; |
| }; |
| |
| static void |
| _rw_vector_sigcat (char** pbuf, size_t * pbufsize, VectorIds::OverloadId which, |
| bool self, const char* str, size_t str_len, |
| const char* arg, size_t arg_len, |
| const ContainerTestCase &tcase) |
| { |
| // temporary empty |
| |
| _RWSTD_UNUSED (pbuf); |
| _RWSTD_UNUSED (pbufsize); |
| _RWSTD_UNUSED (which); |
| _RWSTD_UNUSED (self); |
| _RWSTD_UNUSED (str); |
| _RWSTD_UNUSED (str_len); |
| _RWSTD_UNUSED (arg); |
| _RWSTD_UNUSED (arg_len); |
| _RWSTD_UNUSED (tcase); |
| } |
| |
| /**************************************************************************/ |
| |
| // temporary, should be defined in 23.deque.h |
| struct DequeIds : ContainerIds |
| { |
| enum OverloadId { |
| dummy |
| }; |
| }; |
| |
| static void |
| _rw_deque_sigcat (char** pbuf, size_t * pbufsize, DequeIds::OverloadId which, |
| bool self, const char* str, size_t str_len, |
| const char* arg, size_t arg_len, |
| const ContainerTestCase &tcase) |
| { |
| // temporary empty |
| |
| _RWSTD_UNUSED (pbuf); |
| _RWSTD_UNUSED (pbufsize); |
| _RWSTD_UNUSED (which); |
| _RWSTD_UNUSED (self); |
| _RWSTD_UNUSED (str); |
| _RWSTD_UNUSED (str_len); |
| _RWSTD_UNUSED (arg); |
| _RWSTD_UNUSED (arg_len); |
| _RWSTD_UNUSED (tcase); |
| } |
| |
| /**************************************************************************/ |
| |
| // temporary, should be defined in 23.queue.h |
| struct QueueIds : ContainerIds |
| { |
| enum OverloadId { |
| dummy |
| }; |
| }; |
| |
| static void |
| _rw_queue_sigcat (char** pbuf, size_t * pbufsize, QueueIds::OverloadId which, |
| bool self, const char* str, size_t str_len, |
| const char* arg, size_t arg_len, |
| const ContainerTestCase &tcase) |
| { |
| // temporary empty |
| |
| _RWSTD_UNUSED (pbuf); |
| _RWSTD_UNUSED (pbufsize); |
| _RWSTD_UNUSED (which); |
| _RWSTD_UNUSED (self); |
| _RWSTD_UNUSED (str); |
| _RWSTD_UNUSED (str_len); |
| _RWSTD_UNUSED (arg); |
| _RWSTD_UNUSED (arg_len); |
| _RWSTD_UNUSED (tcase); |
| } |
| |
| /**************************************************************************/ |
| |
| // temporary, should be defined in 23.stack.h |
| struct StackIds : ContainerIds |
| { |
| enum OverloadId { |
| dummy |
| }; |
| }; |
| |
| static void |
| _rw_stack_sigcat (char** pbuf, size_t * pbufsize, StackIds::OverloadId which, |
| bool self, const char* str, size_t str_len, |
| const char* arg, size_t arg_len, |
| const ContainerTestCase &tcase) |
| { |
| // temporary empty |
| |
| _RWSTD_UNUSED (pbuf); |
| _RWSTD_UNUSED (pbufsize); |
| _RWSTD_UNUSED (which); |
| _RWSTD_UNUSED (self); |
| _RWSTD_UNUSED (str); |
| _RWSTD_UNUSED (str_len); |
| _RWSTD_UNUSED (arg); |
| _RWSTD_UNUSED (arg_len); |
| _RWSTD_UNUSED (tcase); |
| } |
| |
| /**************************************************************************/ |
| |
| // sets the {CLASS}, {FUNC}, {FUNCSIG}, and optionally {FUNCALL} |
| // environment variables as follows: |
| // CLASS: the name of cont specialization |
| // FUNC: the name of the cont function |
| // FUNCSIG: the name and signature of a specific overload |
| // of the cont function |
| // FUNCALL: a cont describing the call to the cont function |
| // with function with function arguments expanded (as specified |
| // by the TestCase argument) |
| static void |
| _rw_setvars (const ContainerFunc &func, |
| const ContainerTestCase *pcase = 0) |
| { |
| char* buf = 0; |
| size_t bufsize = 0; |
| |
| const char* const class_name = _rw_class_name (func); |
| |
| if (0 == pcase) { |
| // set the {Elem}, {Allocator}, {Iterator} and {Container} |
| // environment variables to the name of the element type |
| // and the Allocator and Iterator and Container specializations |
| rw_fprintf (0, "%{$Elem!:*}", _rw_elem_names [func.elem_id_]); |
| |
| rw_fprintf (0, "%{$Allocator!:*}", _rw_alloc_names [func.alloc_id_]); |
| |
| rw_fprintf (0, "%{$Iterator!:*}", _rw_iter_names [func.iter_id_]); |
| |
| rw_fprintf (0, "%{$Container!:*}", class_name); |
| |
| // set the {CLASS}, {FUNC}, and {FUNCSIG} environment variables |
| // to the name of the specialization of the template, the name |
| // of the container function, and the name of the overload of the |
| // container function, respectively, when no test case is given |
| |
| // format container specializations, leaving out the name |
| // of the default allocator for brevity |
| rw_asnprintf (&buf, &bufsize, |
| "std::%s<%s%{?}, %s<%2$s>%{;}>", |
| class_name, |
| _rw_elem_names [func.elem_id_], |
| ContainerIds::DefaultAlloc != func.alloc_id_, |
| _rw_alloc_names [func.alloc_id_]); |
| |
| // set the {CLASS} variable to the name of the specialization |
| // of container |
| rw_fprintf (0, "%{$CLASS!:*}", buf); |
| free (buf); |
| buf = 0; |
| bufsize = 0; |
| |
| // determine the container function name |
| const size_t funcinx = func.which_ & ContainerIds::fid_mask; |
| const size_t nfuncs = sizeof _rw_func_names / sizeof *_rw_func_names; |
| |
| RW_ASSERT (funcinx < nfuncs); |
| |
| // get the undecorated function name; ctors are treated |
| // specially so that we can have string, wstring, or |
| // cont, depending on the template arguments |
| const char* const funcname = _rw_func_names [funcinx] ? |
| _rw_func_names [funcinx] : class_name; |
| |
| // determine whether the function is a member function |
| const bool is_member = 0 != (func.which_ & ContainerIds::bit_member); |
| |
| // set the {FUNC} variable to the unqualified/undecorated |
| // name of the container function (member or otherwise) |
| rw_asnprintf (&buf, &bufsize, "%{?}std::%{;}%s", |
| !is_member, funcname); |
| |
| rw_fprintf (0, "%{$FUNC!:*}", buf); |
| |
| // append the function signature |
| _rw_sigcat (&buf, &bufsize, &func); |
| |
| rw_fprintf (0, "%{$FUNCSIG!:*}", buf); |
| free (buf); |
| |
| return; |
| } |
| |
| // do the function call arguments reference *this? |
| const bool self = 0 == pcase->arg; |
| |
| char str_buf [256]; |
| char arg_buf [256]; |
| |
| char *str; |
| char *arg; |
| |
| size_t str_len = sizeof str_buf; |
| size_t arg_len = sizeof arg_buf; |
| |
| if (pcase->str) |
| str = rw_expand (str_buf, pcase->str, pcase->str_len, &str_len); |
| else |
| str = 0; |
| |
| if (pcase->arg) |
| arg = rw_expand (arg_buf, pcase->arg, pcase->arg_len, &arg_len); |
| else |
| arg = 0; |
| |
| // determine whether the function is a member function |
| const bool is_member = 0 != (func.which_ & ContainerIds::bit_member); |
| |
| // determine whether the function is a ctor |
| bool is_ctor = ContainerIds::fid_ctor == (func.which_ & ContainerIds::fid_mask); |
| |
| if (is_ctor) { |
| // for ctors append just the class name here |
| // the class name will inserted below during argument |
| // formatting |
| rw_asnprintf (&buf, &bufsize, "%{$CLASS}::%s", class_name); |
| } |
| else if (is_member) { |
| // for other members append the ctor argument(s) followed |
| // by the cont member function name |
| rw_asnprintf (&buf, &bufsize, |
| "%{$CLASS} (%{?}%{#*s}%{;}).%{$FUNC}", |
| str != 0, int (str_len), str); |
| } |
| else { |
| // for non-members append just the function name here |
| // the class name will inserted below during argument |
| // formatting |
| rw_asnprintf (&buf, &bufsize, "%{$FUNC}"); |
| } |
| |
| // format and append container function arguments abbreviating complex |
| // expressions as much as possible to make them easy to understand |
| switch (func.cont_id_) { |
| |
| case ContainerIds::List: |
| _rw_list_sigcat (&buf, &bufsize, |
| _RWSTD_STATIC_CAST (ListIds::OverloadId, func.which_), |
| self, str, str_len, arg, arg_len, *pcase); |
| break; |
| |
| case ContainerIds::Vector: |
| _rw_vector_sigcat (&buf, &bufsize, |
| _RWSTD_STATIC_CAST (VectorIds::OverloadId, func.which_), |
| self, str, str_len, arg, arg_len, *pcase); |
| break; |
| |
| case ContainerIds::Deque: |
| _rw_deque_sigcat (&buf, &bufsize, |
| _RWSTD_STATIC_CAST (DequeIds::OverloadId, func.which_), |
| self, str, str_len, arg, arg_len, *pcase); |
| break; |
| |
| case ContainerIds::Queue: |
| _rw_queue_sigcat (&buf, &bufsize, |
| _RWSTD_STATIC_CAST (QueueIds::OverloadId, func.which_), |
| self, str, str_len, arg, arg_len, *pcase); |
| break; |
| |
| case ContainerIds::Stack: |
| _rw_stack_sigcat (&buf, &bufsize, |
| _RWSTD_STATIC_CAST (StackIds::OverloadId, func.which_), |
| self, str, str_len, arg, arg_len, *pcase); |
| break; |
| |
| default: |
| RW_ASSERT (!"unknown container id"); |
| } |
| |
| rw_fprintf (0, "%{$FUNCALL!:*}", buf); |
| free (buf); |
| |
| if (str != str_buf) |
| delete[] str; |
| |
| if (arg != arg_buf) |
| delete[] arg; |
| } |
| |
| /**************************************************************************/ |
| |
| // helper function to reverse substring in the resulting sequence |
| _RWSTD_INTERNAL ContainerTestCase |
| _rw_reverse_results (const ContainerTestCase &tsrc, |
| size_t off, size_t ext) |
| { |
| // expand expected results |
| size_t res_len = 0; |
| char* const new_res = rw_expand ((char*)0, tsrc.res, tsrc.nres, &res_len); |
| |
| // reverse them |
| const size_t res_off = off; |
| const size_t res_ext = (ext < res_len ? ext : res_len) - 1; |
| |
| char* beg = new_res + res_off; |
| char* end = beg + res_ext; |
| |
| for (; beg < end; ++beg, --end) { |
| const char tmp = *beg; |
| *beg = *end; |
| *end = tmp; |
| } |
| |
| // form new test case |
| const ContainerTestCase new_case = { |
| tsrc.line, tsrc.off, tsrc.size, tsrc.off2, |
| tsrc.size2, tsrc.val, tsrc.str, tsrc.str_len, |
| tsrc.arg, tsrc.arg_len, new_res, res_len, tsrc.bthrow |
| }; |
| |
| return new_case; |
| } |
| |
| |
| template <class T, class Allocator> |
| void |
| _rw_dispatch (T*, Allocator*, |
| VoidFunc* const *farray, |
| const ContainerFunc &func, |
| const ContainerTestCase &tcase) |
| { |
| typedef ContainerTestCaseData<T> Data; |
| typedef void TestFunc (T*, Allocator*, const Data&); |
| |
| const size_t inx = func.elem_id_ * 2 + func.alloc_id_; |
| |
| TestFunc* const tfunc = _RWSTD_REINTERPRET_CAST (TestFunc*, farray [inx]); |
| |
| if (0 == tfunc) { |
| rw_error (0, __FILE__, __LINE__, |
| "logic error: null test function for %{$FUNCSIG}"); |
| return; |
| } |
| |
| const bool reverse_iter = |
| ContainerIds::ReverseIterator == func.iter_id_ |
| || ContainerIds::ConstReverseIterator == func.iter_id_; |
| |
| const Data tdata (func, tcase); |
| |
| if (reverse_iter) { |
| // special processing for reverse iterators |
| |
| const size_t func_id = tdata.func_.which_ & ContainerIds::fid_mask; |
| |
| const bool like_ctor = |
| ContainerIds::fid_ctor == func_id |
| || ContainerIds::fid_assign == func_id; |
| |
| // ctor and assignment operator require the full container reverse |
| const size_t off = like_ctor ? 0 : tdata.off1_; |
| const size_t ext = like_ctor ? tdata.reslen_ : tdata.ext2_; |
| |
| const ContainerTestCase rev_tcase = |
| _rw_reverse_results (tcase, off, ext); |
| |
| const Data rev_tdata (func, rev_tcase); |
| |
| tfunc ((T*)0, (Allocator*)0, rev_tdata); |
| |
| // clean up allocated memory, if any |
| delete[] rev_tcase.res; |
| } |
| else |
| tfunc ((T*)0, (Allocator*)0, tdata); |
| } |
| |
| |
| template <class T> |
| void |
| _rw_dispatch (T*, |
| VoidFunc* const *farray, |
| const ContainerFunc &func, |
| const ContainerTestCase &tcase) |
| { |
| if (ContainerIds::DefaultAlloc == func.alloc_id_) { |
| typedef std::allocator<T> Alloc; |
| _rw_dispatch ((T*)0, (Alloc*)0, farray, func, tcase); |
| } |
| else if (ContainerIds::UserAlloc == func.alloc_id_) { |
| typedef UserAlloc<T> Alloc; |
| _rw_dispatch ((T*)0, (Alloc*)0, farray, func, tcase); |
| } |
| else { |
| RW_ASSERT (!"logic error: unknown Allocator argument"); |
| } |
| } |
| |
| |
| static void |
| _rw_dispatch (VoidFunc* const *farray, |
| const ContainerFunc &func, |
| const ContainerTestCase &tcase) |
| { |
| if (ContainerIds::UserPOD == func.elem_id_) { |
| _rw_dispatch ((UserPOD*)0, farray, func, tcase); |
| } |
| else if (ContainerIds::UserClass == func.elem_id_) { |
| _rw_dispatch ((UserClass*)0, farray, func, tcase); |
| } |
| else { |
| RW_ASSERT (!"logic error: unknown T argument"); |
| } |
| } |
| |
| /**************************************************************************/ |
| |
| // exercise a single test case for the given function |
| static void |
| _rw_test_case (const ContainerFunc &func, |
| const ContainerTestCase &tcase, |
| ContainerTestFunc *test_callback, |
| VoidFunc* const *farray) |
| { |
| // check to see if this is an exception safety test case |
| // and avoid running it when exception safety has been |
| // disabled via a command line option |
| if (-1 == tcase.bthrow && _rw_opt_no_exception_safety) { |
| |
| // issue only the first note |
| rw_note (1 < _rw_opt_no_exception_safety++, _rw_this_file, __LINE__, |
| "exception safety tests disabled"); |
| return; |
| } |
| |
| // check to see if this is a test case that involves the throwing |
| // of an exception and avoid running it when exceptions have been |
| // disabled |
| if (tcase.bthrow && _rw_opt_no_exceptions) { |
| |
| // issue only the first note |
| rw_note (1 < _rw_opt_no_exceptions++, _rw_this_file, __LINE__, |
| "exception tests disabled"); |
| return; |
| } |
| |
| const bool self_ref = 0 == tcase.arg; |
| |
| // check for tests exercising self-referential modifications |
| // (e.g., insert(1, *this)) |
| if (_rw_opt_self_ref < 0 && self_ref) { |
| // issue only the first note |
| rw_note (0, _rw_this_file, tcase.line, |
| "self-referential test disabled"); |
| return; |
| } |
| else if (0 < _rw_opt_self_ref && !self_ref) { |
| // issue only the first note |
| rw_note (0, _rw_this_file, tcase.line, |
| "non-self-referential test disabled"); |
| return; |
| } |
| |
| // check to see if the test case is enabled |
| if (rw_enabled (tcase.line)) { |
| |
| // set the {FUNCALL} environment variable to describe |
| // the function call specified by this test case |
| _rw_setvars (func, &tcase); |
| |
| if (test_callback) { |
| // invoke the test callback function |
| test_callback (func, tcase); |
| } |
| else { |
| _rw_dispatch (farray, func, tcase); |
| } |
| } |
| else |
| rw_note (0, _rw_this_file, tcase.line, |
| "test on line %d disabled", tcase.line); |
| } |
| |
| /**************************************************************************/ |
| |
| static ContainerIds::ContainerId |
| _rw_container_id; |
| |
| static ContainerTestFunc* |
| _rw_test_callback; |
| |
| |
| static VoidFunc* const* |
| _rw_func_array; |
| |
| |
| // exercise all test cases defined for the given function |
| static void |
| _rw_run_cases (const ContainerFunc &func, |
| const ContainerTest &test) |
| { |
| // set the {CLASS}, {FUNC}, and {FUNCSIG} environment |
| // variable to the name of the container specialization |
| // and the container function being exercised |
| _rw_setvars (func); |
| |
| // determine whether the function is a member function |
| const bool is_member = 0 != (ContainerIds::bit_member & test.which); |
| |
| // compute the function overload's 0-based index |
| const size_t siginx = _rw_get_func_inx (test.which); |
| |
| // check if tests of the function overload |
| // have been disabled |
| if (0 == rw_note (0 <= _rw_opt_func [siginx], _rw_this_file, __LINE__, |
| "%{?}%{$CLASS}::%{;}%{$FUNCSIG} tests disabled", |
| is_member)) |
| return; |
| |
| rw_info (0, 0, 0, "%{?}%{$CLASS}::%{;}%{$FUNCSIG}", is_member); |
| |
| const size_t case_count = test.case_count; |
| |
| // iterate over all test cases for this function |
| // overload invoking the test case handler for each |
| // in turn |
| for (size_t n = 0; n != case_count; ++n) { |
| |
| const ContainerTestCase& tcase = test.cases [n]; |
| |
| _rw_test_case (func, tcase, _rw_test_callback, _rw_func_array); |
| } |
| } |
| |
| /**************************************************************************/ |
| |
| void |
| _rw_toggle_options (int *opts, size_t count) |
| { |
| for (size_t i = 0; i != count; ++i) { |
| if (0 < opts [i]) { |
| // if one or more options has been explicitly enabled |
| // treat all those that haven't been as if they had |
| // been disabled |
| for (i = 0; i != count; ++i) { |
| if (0 == opts [i]) |
| opts [i] = -1; |
| } |
| break; |
| } |
| } |
| } |
| |
| |
| static int |
| _rw_run_test (int, char*[]) |
| { |
| #ifdef _RWSTD_NO_EXCEPTIONS |
| |
| rw_note (0, 0, 0, "exception tests disabled (macro " |
| "_RWSTD_NO_EXCEPTIONS #defined)"); |
| |
| // disable all exception tests and avoid further notes |
| _rw_no_exceptions = 2; |
| _rw_no_exception_safety = 2; |
| |
| #endif // _RWSTD_NO_EXCEPTIONS |
| |
| // see if any option controlling a container function has been |
| // explicitly enabled and if so disable all those that haven't |
| // been (i.e., so that --enable-foo-size will have the effect |
| // of specifying --disable-foo-val and --disable-foo-range, |
| // given the three overloads of foo) |
| const size_t nopts = sizeof _rw_opt_func / sizeof *_rw_opt_func; |
| _rw_toggle_options (_rw_opt_func, nopts); |
| |
| static const ContainerIds::ElemId elem_types[] = { |
| ContainerIds::UserPOD, ContainerIds::UserClass |
| }; |
| |
| static const ContainerIds::AllocId alloc_types[] = { |
| ContainerIds::DefaultAlloc, ContainerIds::UserAlloc |
| }; |
| |
| static const ContainerIds::IteratorId iter_types[] = { |
| ContainerIds::Input, ContainerIds::Forward, |
| ContainerIds::Bidir, ContainerIds::Random, |
| ContainerIds::Pointer, ContainerIds::ConstPointer, |
| ContainerIds::Iterator, ContainerIds::ConstIterator, |
| ContainerIds::ReverseIterator, ContainerIds::ConstReverseIterator |
| }; |
| |
| const size_t n_elem_types = sizeof elem_types / sizeof *elem_types; |
| const size_t n_alloc_types = sizeof alloc_types / sizeof *alloc_types; |
| const size_t n_iter_types = sizeof iter_types / sizeof *iter_types; |
| |
| // see if any option controlling the container template arguments |
| // explicitly enabled and if so disable all those that haven't been |
| _rw_toggle_options (_rw_opt_elem_types, n_elem_types); |
| _rw_toggle_options (_rw_opt_alloc_types, n_alloc_types); |
| _rw_toggle_options (_rw_opt_iter_types, n_iter_types); |
| |
| // exercise different T specializations last |
| for (size_t i = 0; i != n_elem_types; ++i) { |
| |
| if (_rw_opt_elem_types [i] < 0) { |
| // issue only the first note |
| rw_note (-1 > _rw_opt_elem_types [i]--, |
| _rw_this_file, __LINE__, |
| "%s tests disabled", _rw_elem_names [i]); |
| continue; |
| } |
| |
| for (size_t k = 0; k != n_alloc_types; ++k) { |
| |
| if (_rw_opt_alloc_types [k] < 0) { |
| // issue only the first note |
| rw_note (-1 > _rw_opt_alloc_types [k]--, |
| _rw_this_file, __LINE__, |
| "%s tests disabled", _rw_alloc_names [k]); |
| continue; |
| } |
| |
| for (size_t m = 0; m != _rw_cont_test_count; ++m) { |
| |
| const ContainerTest& test = _rw_cont_tests [m]; |
| |
| // create an object uniquely identifying the overload |
| // of the container function exercised by the set of test |
| // cases defined to exercise it |
| ContainerFunc func = { |
| elem_types [i], |
| alloc_types [k], |
| ContainerIds::None, |
| _rw_container_id, |
| test.which |
| }; |
| |
| // determine whether the function is a template |
| if (-1 < _rw_argno (test.which, ContainerIds::arg_range)) { |
| |
| // iterate over the standard iterator categories |
| // and iterator types the template might perhaps |
| // be specialized on |
| for (size_t l = 0; l != n_iter_types; ++l) { |
| |
| if (_rw_opt_iter_types [l] < 0) { |
| // issue only the first note |
| rw_note (-1 > _rw_opt_iter_types [l]--, |
| _rw_this_file, __LINE__, |
| "%s tests disabled", |
| _rw_iter_names [l]); |
| continue; |
| } |
| |
| func.iter_id_ = iter_types [l]; |
| |
| // exercise all test cases defined for |
| // the function template |
| _rw_run_cases (func, test); |
| } |
| } |
| else { |
| // exercise all test cases defined for the ordinary |
| // (i.e., non-template) function |
| _rw_run_cases (func, test); |
| } |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************/ |
| |
| // add a bunch of toggle-type command line options based on the names array |
| static void |
| _rw_add_toggles (char **pbuf, size_t *pbufsize, |
| const char* const names[], size_t count) |
| { |
| for (size_t i = 0; i != count; ++i) { |
| rw_asnprintf (pbuf, pbufsize, "%{+}|-%s~ ", names [i]); |
| } |
| } |
| |
| static void |
| _rw_add_container_toggles (char **pbuf, size_t *pbufsize) |
| { |
| _rw_add_toggles (pbuf, pbufsize, _rw_alloc_names, |
| sizeof _rw_alloc_names / sizeof *_rw_alloc_names); |
| _rw_add_toggles (pbuf, pbufsize, _rw_iter_names + 1, |
| sizeof _rw_iter_names / sizeof *_rw_iter_names - 1); |
| } |
| |
| |
| static int |
| _rw_run_test (int argc, |
| char *argv [], |
| const char *file, |
| const char *clause, |
| ContainerIds::ContainerId container, |
| ContainerTestFunc *test_callback, |
| VoidFunc* const *func_array, |
| const ContainerTest *tests, |
| size_t test_count) |
| { |
| // set the global variables accessed in _rw_run_test |
| _rw_container_id = container; |
| _rw_test_callback = test_callback; |
| _rw_func_array = func_array, |
| _rw_cont_tests = tests; |
| _rw_cont_test_count = test_count; |
| |
| // put together a command line option specification with options |
| // to enable and disable tests exercising functions for all known |
| // specializations of the functions specified by the test array |
| char *optbuf = 0; |
| size_t optbufsize = 0; |
| |
| rw_asnprintf (&optbuf, &optbufsize, |
| "|-no-exceptions# " |
| "|-no-exception-safety# " |
| "|-self-ref~ "); |
| |
| const size_t n_elems = sizeof _rw_elem_names / sizeof *_rw_elem_names; |
| |
| // see if any option has been explicitly enabled and if so, |
| // unconditionally disable all those that have not been |
| _rw_add_toggles (&optbuf, &optbufsize, _rw_elem_names, n_elems); |
| _rw_add_container_toggles (&optbuf, &optbufsize); |
| |
| for (size_t i = 0; i != test_count; ++i) { |
| |
| // for each function append a command line option specification |
| // to allow to enable or disable it |
| rw_asnprintf (&optbuf, &optbufsize, "%{+}|-"); |
| _rw_sigcat (&optbuf, &optbufsize, 0, tests [i].which); |
| rw_asnprintf (&optbuf, &optbufsize, "%{+}~ "); |
| } |
| |
| RW_ASSERT (test_count <= 32); |
| RW_ASSERT (test_count <= MAX_OVERLOADS); |
| |
| // process command line arguments run tests |
| const int status = |
| rw_test (argc, argv, file, clause, |
| 0, // comment |
| _rw_run_test, // test callback |
| optbuf, // option specification |
| |
| // handlers controlling exceptions |
| &_rw_opt_no_exceptions, |
| &_rw_opt_no_exception_safety, |
| |
| // handler controlling self-referential modifiers |
| &_rw_opt_self_ref, |
| |
| // handlers controlling specializations of the template |
| // ...on the T template parameter |
| _rw_opt_elem_types + 0, |
| _rw_opt_elem_types + 1, |
| |
| // ...on the Allocator template parameter |
| _rw_opt_alloc_types + 0, |
| _rw_opt_alloc_types + 1, |
| |
| // FIXME: add handlers (and options) only for tests |
| // that exercise member templates |
| |
| // handlers controlling specializations of the member |
| // template (if this is one) on the InputIterator |
| // template parameter |
| _rw_opt_iter_types + 0, |
| _rw_opt_iter_types + 1, |
| _rw_opt_iter_types + 2, |
| _rw_opt_iter_types + 3, |
| _rw_opt_iter_types + 4, |
| _rw_opt_iter_types + 5, |
| _rw_opt_iter_types + 6, |
| _rw_opt_iter_types + 7, |
| _rw_opt_iter_types + 8, |
| _rw_opt_iter_types + 9, |
| |
| // FIXME: install exactly as many handlers (and options) |
| // as there are distinct functions being exercised |
| |
| // handlers for up to 32 overloads |
| _rw_opt_func + 0, |
| _rw_opt_func + 1, |
| _rw_opt_func + 2, |
| _rw_opt_func + 3, |
| _rw_opt_func + 4, |
| _rw_opt_func + 5, |
| _rw_opt_func + 6, |
| _rw_opt_func + 7, |
| _rw_opt_func + 8, |
| _rw_opt_func + 9, |
| _rw_opt_func + 10, |
| _rw_opt_func + 11, |
| _rw_opt_func + 12, |
| _rw_opt_func + 13, |
| _rw_opt_func + 14, |
| _rw_opt_func + 15, |
| _rw_opt_func + 16, |
| _rw_opt_func + 17, |
| _rw_opt_func + 18, |
| _rw_opt_func + 19, |
| _rw_opt_func + 20, |
| _rw_opt_func + 21, |
| _rw_opt_func + 22, |
| _rw_opt_func + 23, |
| _rw_opt_func + 24, |
| _rw_opt_func + 25, |
| _rw_opt_func + 26, |
| _rw_opt_func + 27, |
| _rw_opt_func + 28, |
| _rw_opt_func + 29, |
| _rw_opt_func + 30, |
| _rw_opt_func + 31, |
| |
| // sentinel |
| (void*)0); |
| |
| // free storage allocated for the option specification |
| free (optbuf); |
| |
| return status; |
| } |
| |
| |
| |
| _TEST_EXPORT int |
| rw_run_cont_test (int argc, |
| char *argv [], |
| const char *file, |
| const char *clause, |
| ContainerIds::ContainerId container, |
| ContainerTestFunc *callback, |
| const ContainerTest *tests, |
| size_t count) |
| { |
| return _rw_run_test (argc, argv, file, clause, container, |
| callback, 0, tests, count); |
| } |
| |
| |
| _TEST_EXPORT int |
| rw_run_cont_test (int argc, |
| char *argv [], |
| const char *file, |
| const char *clause, |
| ContainerIds::ContainerId container, |
| VoidFunc* const *farray, |
| const ContainerTest *tests, |
| size_t count) |
| { |
| return _rw_run_test (argc, argv, file, clause, container, |
| 0, farray, tests, count); |
| } |