1 /* 2 Copyright (c) 2005-2021 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #ifndef __TBB_test_common_concurrent_associative_common_H 18 #define __TBB_test_common_concurrent_associative_common_H 19 20 #include "config.h" 21 22 #include "custom_allocators.h" 23 #include "utils.h" 24 #include "utils_concurrency_limit.h" 25 #include "container_move_support.h" 26 #include "checktype.h" 27 #include "range_based_for_support.h" 28 #include "initializer_list_support.h" 29 #include "node_handling_support.h" 30 #include "containers_common.h" 31 #include "test_comparisons.h" 32 #include "concepts_common.h" 33 #include <list> 34 #include <cstring> 35 36 #include "oneapi/tbb/parallel_for.h" 37 38 // This structure should be specialized for multimap and multiset classes 39 template <typename T> 40 struct AllowMultimapping : std::false_type {}; 41 42 template<typename Table> 43 inline void CheckAllocator(typename Table::allocator_type& a, size_t expected_allocs, size_t expected_frees, 44 bool exact = true) { 45 if(exact) { 46 REQUIRE( a.allocations == expected_allocs); 47 REQUIRE( a.frees == expected_frees); 48 } else { 49 REQUIRE( a.allocations >= expected_allocs); 50 REQUIRE( a.frees >= expected_frees); 51 REQUIRE( a.allocations - a.frees == expected_allocs - expected_frees ); 52 } 53 } 54 55 // value generator for maps 56 template <typename Key, typename Value = std::pair<const Key, Key>> 57 struct ValueFactoryBase { 58 using strip_key = typename std::remove_const<Key>::type; 59 static Value make( const Key& key ) { return Value(key, key); } 60 static Value make( const Key& key, const Key& mapped ) { return Value(key, mapped); } 61 static strip_key key( const Value& value ) { return value.first; } 62 static strip_key get( const Value& value ) { return strip_key(value.second); } 63 template <typename U> 64 static U convert( const Value& value ) { return U(value.second); } 65 }; 66 67 template <typename T> 68 struct SpecialTests { 69 static void Test() {} 70 }; 71 72 // value generator for sets 73 template <typename Key> 74 struct ValueFactoryBase<Key, Key> { 75 static Key make( const Key& key ) { return key; } 76 static Key make( const Key& key, const Key& ) { return key; } 77 static Key key( const Key& value ) { return value; } 78 static Key get( const Key& value ) { return value; } 79 template <typename U> 80 static U convert( const Key& value ) { return U(value); } 81 }; 82 83 template <typename Container> 84 struct Value : ValueFactoryBase<typename Container::key_type, typename Container::value_type> { 85 template <typename U> 86 static bool compare( const typename Container::iterator& it, U val ) { 87 return (Value::template convert<U>(*it) == val); 88 } 89 }; 90 91 template <typename Map> 92 void SpecialMapTests(){ 93 Map cont; 94 const Map &ccont( cont ); 95 96 // mapped_type& operator[](const key_type& k); 97 cont[1] = 2; 98 99 // bool empty() const; 100 REQUIRE_MESSAGE( !ccont.empty( ), "Concurrent container empty after adding an element" ); 101 102 // size_type size() const; 103 REQUIRE_MESSAGE( ccont.size( ) == 1, "Concurrent container size incorrect" ); 104 REQUIRE_MESSAGE( cont[1] == 2, "Concurrent container value incorrect" ); 105 106 // mapped_type& at( const key_type& k ); 107 // const mapped_type& at(const key_type& k) const; 108 REQUIRE_MESSAGE( cont.at( 1 ) == 2, "Concurrent container value incorrect" ); 109 REQUIRE_MESSAGE( ccont.at( 1 ) == 2, "Concurrent container value incorrect" ); 110 111 // iterator find(const key_type& k); 112 typename Map::iterator it = cont.find( 1 ); 113 REQUIRE_MESSAGE((it != cont.end( ) && Value<Map>::get( *(it) ) == 2), "Element with key 1 not properly found" ); 114 cont.unsafe_erase( it ); 115 116 it = cont.find( 1 ); 117 REQUIRE_MESSAGE( it == cont.end( ), "Element with key 1 not properly erased" ); 118 } 119 120 template <typename MultiMap> 121 void CheckMultiMap(MultiMap &m, int *targets, int tcount, int key) { 122 std::vector<bool> vfound(tcount,false); 123 std::pair<typename MultiMap::iterator, typename MultiMap::iterator> range = m.equal_range( key ); 124 for(typename MultiMap::iterator it = range.first; it != range.second; ++it) { 125 bool found = false; 126 for( int i = 0; i < tcount; ++i) { 127 if((*it).second == targets[i]) { 128 if(!vfound[i]) { // we can insert duplicate values 129 vfound[i] = found = true; 130 break; 131 } 132 } 133 } 134 // just in case an extra value in equal_range... 135 REQUIRE_MESSAGE(found, "extra value from equal range"); 136 } 137 for(int i = 0; i < tcount; ++i) REQUIRE_MESSAGE(vfound[i], "missing value"); 138 } 139 140 template <typename MultiMap> 141 void MultiMapEraseTests(){ 142 MultiMap cont1, cont2; 143 144 // Assignment to begin to avoid maybe-uninitialized warning 145 typename MultiMap::iterator erased_it = cont1.begin(); 146 for (int i = 0; i < 10; ++i) { 147 if ( i != 1 ) { 148 cont1.insert(std::make_pair(1, i)); 149 cont2.insert(std::make_pair(1, i)); 150 } else { 151 erased_it = cont1.insert(std::make_pair(1, i)).first; 152 } 153 } 154 155 cont1.unsafe_erase(erased_it); 156 157 REQUIRE_MESSAGE(cont1.size() == cont2.size(), "Incorrect count of elements was erased"); 158 typename MultiMap::iterator it1 = cont1.begin(); 159 typename MultiMap::iterator it2 = cont2.begin(); 160 161 for (typename MultiMap::size_type i = 0; i < cont2.size(); ++i) { 162 REQUIRE_MESSAGE((*(it1++) == *(it2++)), "Multimap repetitive key was not erased properly"); 163 } 164 } 165 166 template <typename MultiMap> 167 void SpecialMultiMapTests(){ 168 int one_values[] = { 7, 2, 13, 23, 13 }; 169 int zero_values[] = { 4, 9, 13, 29, 42, 111}; 170 int n_zero_values = sizeof(zero_values) / sizeof(int); 171 int n_one_values = sizeof(one_values) / sizeof(int); 172 MultiMap cont; 173 const MultiMap &ccont( cont ); 174 // mapped_type& operator[](const key_type& k); 175 cont.insert( std::make_pair( 1, one_values[0] ) ); 176 177 // bool empty() const; 178 REQUIRE_MESSAGE( !ccont.empty( ), "Concurrent container empty after adding an element" ); 179 180 // size_type size() const; 181 REQUIRE_MESSAGE( ccont.size( ) == 1, "Concurrent container size incorrect" ); 182 REQUIRE_MESSAGE( (*(cont.begin( ))).second == one_values[0], "Concurrent container value incorrect" ); 183 REQUIRE_MESSAGE( (*(cont.equal_range( 1 )).first).second == one_values[0], "Improper value from equal_range" ); 184 REQUIRE_MESSAGE( ((cont.equal_range( 1 )).second == cont.end( )), "Improper iterator from equal_range" ); 185 186 cont.insert( std::make_pair( 1, one_values[1] ) ); 187 188 // bool empty() const; 189 REQUIRE_MESSAGE( !ccont.empty( ), "Concurrent container empty after adding an element" ); 190 191 // size_type size() const; 192 REQUIRE_MESSAGE( ccont.size( ) == 2, "Concurrent container size incorrect" ); 193 CheckMultiMap(cont, one_values, 2, 1); 194 195 // insert the other {1,x} values 196 for( int i = 2; i < n_one_values; ++i ) { 197 cont.insert( std::make_pair( 1, one_values[i] ) ); 198 } 199 200 CheckMultiMap(cont, one_values, n_one_values, 1); 201 REQUIRE_MESSAGE( (cont.equal_range( 1 )).second == cont.end( ), "Improper iterator from equal_range" ); 202 203 cont.insert( std::make_pair( 0, zero_values[0] ) ); 204 205 // bool empty() const; 206 REQUIRE_MESSAGE( !ccont.empty( ), "Concurrent container empty after adding an element" ); 207 208 // size_type size() const; 209 REQUIRE_MESSAGE( ccont.size( ) == (size_t)(n_one_values+1), "Concurrent container size incorrect" ); 210 CheckMultiMap(cont, one_values, n_one_values, 1); 211 CheckMultiMap(cont, zero_values, 1, 0); 212 REQUIRE_MESSAGE( (*cont.find(0)).second == zero_values[0], "Concurrent container value incorrect"); 213 // insert the rest of the zero values 214 for( int i = 1; i < n_zero_values; ++i) { 215 cont.insert( std::make_pair( 0, zero_values[i] ) ); 216 } 217 CheckMultiMap(cont, one_values, n_one_values, 1); 218 CheckMultiMap(cont, zero_values, n_zero_values, 0); 219 220 // clear, reinsert interleaved 221 cont.clear(); 222 int bigger_num = ( n_one_values > n_zero_values ) ? n_one_values : n_zero_values; 223 for( int i = 0; i < bigger_num; ++i ) { 224 if(i < n_one_values) cont.insert( std::make_pair( 1, one_values[i] ) ); 225 if(i < n_zero_values) cont.insert( std::make_pair( 0, zero_values[i] ) ); 226 } 227 CheckMultiMap(cont, one_values, n_one_values, 1); 228 CheckMultiMap(cont, zero_values, n_zero_values, 0); 229 230 MultiMapEraseTests<MultiMap>(); 231 232 } 233 234 template <StateTrackableBase::StateValue desired_state, typename T> 235 void check_value_state( const T& t, std::true_type ) { 236 REQUIRE_MESSAGE(is_state_predicate<desired_state>{}(t), "Unexpected value state"); 237 } 238 239 template <StateTrackableBase::StateValue desired_state, typename T> 240 void check_value_state(const T&, std::false_type) {} 241 242 template <typename Container, typename CheckElementState, typename Key> 243 void test_rvalue_insert( Key k1, Key k2 ) { 244 Container cont; 245 246 std::pair<typename Container::iterator, bool> ins = cont.insert(Value<Container>::make(k1)); 247 REQUIRE_MESSAGE(ins.second, "Element 1 has not been inserted"); 248 REQUIRE_MESSAGE(Value<Container>::get(*ins.first) == k1, "Element 1 has not been inserted"); 249 check_value_state<StateTrackableBase::MoveInitialized>(*ins.first, CheckElementState{}); 250 251 typename Container::iterator it2 = cont.insert(ins.first, Value<Container>::make(k2)); 252 REQUIRE_MESSAGE(Value<Container>::get(*it2) == k2, "Element 2 has not been inserted"); 253 check_value_state<StateTrackableBase::MoveInitialized>(*it2, CheckElementState{}); 254 255 } 256 257 namespace emplace_helpers { 258 259 template <typename Container, typename Arg, typename Value> 260 std::pair<typename Container::iterator, bool> call_emplace_impl( Container& c, Arg&& k, Value* ) { 261 // Call emplace implementation for sets 262 return c.emplace(std::forward<Arg>(k)); 263 } 264 265 template <typename Container, typename Arg, typename FirstType, typename SecondType> 266 std::pair<typename Container::iterator, bool> call_emplace_impl( Container& c, Arg&& k, std::pair<FirstType, SecondType>* ) { 267 // Call emplace implementation for maps 268 return c.emplace(k, std::forward<Arg>(k)); 269 } 270 271 template <typename Container, typename Arg, typename Value> 272 typename Container::iterator call_emplace_hint_impl( Container& c, typename Container::const_iterator hint, 273 Arg&& k, Value* ) 274 { 275 // Call emplace_hint implementation for sets 276 return c.emplace_hint(hint, std::forward<Arg>(k)); 277 } 278 279 template <typename Container, typename Arg, typename FirstType, typename SecondType> 280 typename Container::iterator call_emplace_hint_impl( Container& c, typename Container::const_iterator hint, 281 Arg&& k, std::pair<FirstType, SecondType>* ) 282 { 283 // Call emplace_hint implementation for maps 284 return c.emplace_hint(hint, k, std::forward<Arg>(k)); 285 } 286 287 template <typename Container, typename Arg> 288 std::pair<typename Container::iterator, bool> call_emplace( Container& c, Arg&& k ) { 289 typename Container::value_type* selector = nullptr; 290 return call_emplace_impl(c, std::forward<Arg>(k), selector); 291 } 292 293 template <typename Container, typename Arg> 294 typename Container::iterator call_emplace_hint( Container& c, typename Container::const_iterator hint, Arg&& k ) { 295 typename Container::value_type* selector = nullptr; 296 return call_emplace_hint_impl(c, hint, std::forward<Arg>(k), selector); 297 } 298 299 } // namespace emplace_helpers 300 301 template <typename Container, typename CheckElementState, typename Key> 302 void test_emplace_insert( Key key1, Key key2 ) { 303 Container cont; 304 305 std::pair<typename Container::iterator, bool> ins = emplace_helpers::call_emplace(cont, key1); 306 REQUIRE_MESSAGE(ins.second, "Element 1 has not been inserted"); 307 REQUIRE_MESSAGE(Value<Container>::compare(ins.first, key1), "Element 1 has not been inserted"); 308 check_value_state<StateTrackableBase::DirectInitialized>(*ins.first, CheckElementState{}); 309 310 //if (!AllowMultimapping<Container>::value) { 311 // std::pair<typename Container::iterator, bool> rep_ins = emplace_helpers::call_emplace(cont, key1); 312 // REQUIRE_MESSAGE(!rep_ins.second, "Element 1 has been emplaced twice into the container with unique keys"); 313 // REQUIRE(Value<Container>::compare(rep_ins.first, key1)); 314 //} 315 316 typename Container::iterator it2 = emplace_helpers::call_emplace_hint(cont, ins.first, key2); 317 REQUIRE_MESSAGE(Value<Container>::compare(it2, key2), "Element 2 has not been inserted"); 318 check_value_state<StateTrackableBase::DirectInitialized>(*it2, CheckElementState{}); 319 } 320 321 template <typename Container, typename Iterator, typename Range> 322 std::pair<intptr_t, intptr_t> CheckRecursiveRange( Range range ) { 323 std::pair<intptr_t, intptr_t> sum(0, 0); // count, sum 324 325 for (Iterator i = range.begin(); i != range.end(); ++i) { 326 ++sum.first; 327 sum.second += Value<Container>::get(*i); 328 } 329 330 if (range.is_divisible()) { 331 Range range2(range, tbb::split{}); 332 333 auto sum1 = CheckRecursiveRange<Container, Iterator>(range); 334 auto sum2 = CheckRecursiveRange<Container, Iterator>(range2); 335 sum1.first += sum2.first; sum1.second += sum2.second; 336 REQUIRE_MESSAGE(sum == sum1, "Mismatched ranges afted division"); 337 } 338 return sum; 339 } 340 341 using atomic_byte_type = std::atomic<unsigned char>; 342 343 void CheckRange( atomic_byte_type array[], int n, bool allow_multimapping, int odd_count ) { 344 if (allow_multimapping) { 345 for (int k = 0; k < n; ++k) { 346 if (k % 2) { 347 REQUIRE(array[k] == odd_count); 348 } else { 349 REQUIRE(array[k] == 2); 350 } 351 } 352 } else { 353 for (int k = 0; k < n; ++k) { 354 REQUIRE(array[k] == 1); 355 } 356 } 357 } 358 359 template <typename T> 360 void check_equal( const T& cont1, const T& cont2 ) { 361 REQUIRE_MESSAGE(cont1 == cont2, "Containers should be equal"); 362 REQUIRE_MESSAGE(cont2 == cont1, "Containers should be equal"); 363 REQUIRE_MESSAGE(!(cont1 != cont2), "Containers should not be unequal"); 364 REQUIRE_MESSAGE(!(cont2 != cont1), "Containers should not be unequal"); 365 } 366 367 template <typename T> 368 void check_unequal( const T& cont1, const T& cont2 ) { 369 REQUIRE_MESSAGE(cont1 != cont2, "Containers should be unequal"); 370 REQUIRE_MESSAGE(cont2 != cont1, "Containers should be unequal"); 371 REQUIRE_MESSAGE(!(cont1 == cont2), "Containers should not be equal"); 372 REQUIRE_MESSAGE(!(cont2 == cont1), "Containers shuold not be equal"); 373 } 374 375 // Break value for maps 376 template <typename First, typename Second> 377 void break_value( std::pair<First, Second>& value ) { 378 ++value.second; 379 } 380 381 template <typename First> 382 void break_value( std::pair<First, move_support_tests::FooWithAssign>& value ) { 383 ++value.second.bar(); 384 } 385 386 // Break value for sets 387 template <typename T> 388 void break_value( T& value ) { 389 ++value; 390 } 391 392 void break_value( move_support_tests::FooWithAssign& value ) { 393 ++value.bar(); 394 } 395 396 template <typename T> 397 void test_comparison_operators() { 398 T cont; 399 check_equal(cont, cont); 400 401 cont.insert(Value<T>::make(1)); 402 cont.insert(Value<T>::make(2)); 403 404 T cont2 = cont; 405 check_equal(cont, cont2); 406 407 T cont3; 408 check_unequal(cont, cont3); 409 410 T cont4; 411 cont4.insert(Value<T>::make(1)); 412 cont4.insert(Value<T>::make(2)); 413 check_equal(cont, cont4); 414 415 T cont5; 416 cont5.insert(Value<T>::make(1)); 417 cont5.insert(Value<T>::make(3)); 418 check_unequal(cont, cont5); 419 420 T cont6; 421 cont6.insert(Value<T>::make(1)); 422 auto value2 = Value<T>::make(2); 423 break_value(value2); 424 cont6.insert(value2); 425 check_unequal(cont, cont6); 426 } 427 428 template<typename T, typename CheckElementState> 429 void test_basic_common() 430 { 431 T cont; 432 const T &ccont(cont); 433 CheckNoAllocations(cont); 434 // bool empty() const; 435 REQUIRE_MESSAGE(ccont.empty(), "Concurrent container is not empty after construction"); 436 437 // size_type size() const; 438 REQUIRE_MESSAGE(ccont.size() == 0, "Concurrent container is not empty after construction"); 439 440 // size_type max_size() const; 441 REQUIRE_MESSAGE(ccont.max_size() > 0, "Concurrent container max size is invalid"); 442 443 //iterator begin(); 444 //iterator end(); 445 REQUIRE_MESSAGE(cont.begin() == cont.end(), "Concurrent container iterators are invalid after construction"); 446 REQUIRE_MESSAGE(ccont.begin() == ccont.end(), "Concurrent container iterators are invalid after construction"); 447 REQUIRE_MESSAGE(cont.cbegin() == cont.cend(), "Concurrent container iterators are invalid after construction"); 448 449 //std::pair<iterator, bool> insert(const value_type& obj); 450 std::pair<typename T::iterator, bool> ins = cont.insert(Value<T>::make(1)); 451 REQUIRE_MESSAGE((ins.second == true && Value<T>::get(*(ins.first)) == 1), "Element 1 has not been inserted properly"); 452 453 test_rvalue_insert<T,CheckElementState>(1,2); 454 test_emplace_insert<T,CheckElementState>(1,2); 455 456 // bool empty() const; 457 REQUIRE_MESSAGE(!ccont.empty(), "Concurrent container is empty after adding an element"); 458 459 // size_type size() const; 460 REQUIRE_MESSAGE(ccont.size() == 1, "Concurrent container size is incorrect"); 461 462 std::pair<typename T::iterator, bool> ins2 = cont.insert(Value<T>::make(1)); 463 464 if (AllowMultimapping<T>::value) 465 { 466 // std::pair<iterator, bool> insert(const value_type& obj); 467 REQUIRE_MESSAGE((ins2.second == true && Value<T>::get(*(ins2.first)) == 1), "Element 1 has not been inserted properly"); 468 469 // size_type size() const; 470 REQUIRE_MESSAGE(ccont.size() == 2, "Concurrent container size is incorrect"); 471 472 // size_type count(const key_type& k) const; 473 REQUIRE_MESSAGE(ccont.count(1) == 2, "Concurrent container count(1) is incorrect"); 474 // std::pair<iterator, iterator> equal_range(const key_type& k); 475 std::pair<typename T::iterator, typename T::iterator> range = cont.equal_range(1); 476 typename T::iterator it; 477 it = range.first; 478 REQUIRE_MESSAGE((it != cont.end() && Value<T>::get(*it) == 1), "Element 1 has not been found properly"); 479 unsigned int count = 0; 480 for (; it != range.second; it++) 481 { 482 count++; 483 REQUIRE_MESSAGE((Value<T>::get(*it) == 1), "Element 1 has not been found properly"); 484 } 485 486 REQUIRE_MESSAGE(count == 2, "Range doesn't have the right number of elements"); 487 } 488 else 489 { 490 // std::pair<iterator, bool> insert(const value_type& obj); 491 REQUIRE_MESSAGE((ins2.second == false && ins2.first == ins.first), "Element 1 should not be re-inserted"); 492 493 // size_type size() const; 494 REQUIRE_MESSAGE(ccont.size() == 1, "Concurrent container size is incorrect"); 495 496 // size_type count(const key_type& k) const; 497 REQUIRE_MESSAGE(ccont.count(1) == 1, "Concurrent container count(1) is incorrect"); 498 499 // std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const; 500 // std::pair<iterator, iterator> equal_range(const key_type& k); 501 std::pair<typename T::iterator, typename T::iterator> range = cont.equal_range(1); 502 typename T::iterator it = range.first; 503 REQUIRE_MESSAGE((it != cont.end() && Value<T>::get(*it) == 1), "Element 1 has not been found properly"); 504 REQUIRE_MESSAGE((++it == range.second), "Range doesn't have the right number of elements"); 505 } 506 507 // const_iterator find(const key_type& k) const; 508 // iterator find(const key_type& k); 509 typename T::iterator it = cont.find(1); 510 REQUIRE_MESSAGE((it != cont.end() && Value<T>::get(*(it)) == 1), "Element 1 has not been found properly"); 511 REQUIRE_MESSAGE(ccont.find(1) == it, "Element 1 has not been found properly"); 512 513 //bool contains(const key_type&k) const 514 REQUIRE_MESSAGE(cont.contains(1), "contains() cannot detect existing element"); 515 REQUIRE_MESSAGE(!cont.contains(0), "contains() detect not existing element"); 516 517 // iterator insert(const_iterator hint, const value_type& obj); 518 typename T::iterator it2 = cont.insert(ins.first, Value<T>::make(2)); 519 REQUIRE_MESSAGE((Value<T>::get(*it2) == 2), "Element 2 has not been inserted properly"); 520 521 // T(const T& _Umap) 522 T newcont = ccont; 523 524 REQUIRE_MESSAGE((AllowMultimapping<T>{} ? (newcont.size() == 3) : (newcont.size() == 2)), "Copy construction has not copied the elements properly"); 525 526 // size_type unsafe_erase(const key_type& k); 527 typename T::size_type size; 528 #if _MSC_VER && __INTEL_COMPILER == 1900 529 // The compiler optimizes the next line too aggressively. 530 #pragma noinline 531 #endif 532 size = cont.unsafe_erase(1); 533 534 REQUIRE_MESSAGE((AllowMultimapping<T>{} ? (size == 2) : (size == 1)), "Erase has not removed the right number of elements"); 535 536 // iterator unsafe_erase(iterator position); 537 typename T::iterator it4 = cont.unsafe_erase(cont.find(2)); 538 539 REQUIRE_MESSAGE((it4 == cont.end() && cont.size() == 0), "Erase has not removed the last element properly"); 540 541 // iterator unsafe_erase(const_iterator position); 542 cont.insert(Value<T>::make(3)); 543 typename T::iterator it5 = cont.unsafe_erase(cont.cbegin()); 544 REQUIRE_MESSAGE((it5 == cont.end() && cont.size() == 0), "Erase has not removed the last element properly"); 545 546 // template<class InputIterator> void insert(InputIterator first, InputIterator last); 547 548 cont.insert(newcont.begin(), newcont.end()); 549 550 REQUIRE_MESSAGE((AllowMultimapping<T>{} ? (cont.size() == 3) : (cont.size() == 2)), "Range insert has not copied the elements properly"); 551 552 // iterator unsafe_erase(const_iterator first, const_iterator last); 553 std::pair<typename T::iterator, typename T::iterator> range2 = newcont.equal_range(1); 554 555 newcont.unsafe_erase(range2.first, range2.second); 556 REQUIRE_MESSAGE(newcont.size() == 1, "Range erase has not erased the elements properly"); 557 558 // void clear(); 559 newcont.clear(); 560 REQUIRE_MESSAGE((newcont.begin() == newcont.end() && newcont.size() == 0), "Clear has not cleared the container"); 561 562 // void insert(std::initializer_list<value_type> &il); 563 newcont.insert( { Value<T>::make( 1 ), Value<T>::make( 2 ), Value<T>::make( 1 ) } ); 564 if (AllowMultimapping<T>::value) { 565 566 REQUIRE_MESSAGE(newcont.size() == 3, "Concurrent container size is incorrect"); 567 REQUIRE_MESSAGE(newcont.count(1) == 2, "Concurrent container count(1) is incorrect"); 568 REQUIRE_MESSAGE(newcont.count(2) == 1, "Concurrent container count(2) is incorrect"); 569 std::pair<typename T::iterator, typename T::iterator> range = cont.equal_range(1); 570 it = range.first; 571 // REQUIRE_MESSAGE((it != newcont.end() && Value<T>::get(*it) == 1), "Element 1 has not been found properly"); 572 REQUIRE_MESSAGE((it != newcont.end()), "iterator" ); 573 REQUIRE_MESSAGE((Value<T>::get(*it) == 1), "value"); 574 unsigned int count = 0; 575 for (; it != range.second; it++) { 576 count++; 577 REQUIRE_MESSAGE(Value<T>::get(*it) == 1, "Element 1 has not been found properly"); 578 } 579 REQUIRE_MESSAGE(count == 2, "Range doesn't have the right number of elements"); 580 range = newcont.equal_range(2); it = range.first; 581 REQUIRE_MESSAGE((it != newcont.end() && Value<T>::get(*it) == 2), "Element 2 has not been found properly"); 582 count = 0; 583 for (; it != range.second; it++) { 584 count++; 585 REQUIRE_MESSAGE(Value<T>::get(*it) == 2, "Element 2 has not been found properly"); 586 } 587 REQUIRE_MESSAGE(count == 1, "Range doesn't have the right number of elements"); 588 } else { 589 REQUIRE_MESSAGE(newcont.size() == 2, "Concurrent container size is incorrect"); 590 REQUIRE_MESSAGE(newcont.count(1) == 1, "Concurrent container count(1) is incorrect"); 591 REQUIRE_MESSAGE(newcont.count(2) == 1, "Concurrent container count(2) is incorrect"); 592 std::pair<typename T::iterator, typename T::iterator> range = newcont.equal_range(1); 593 it = range.first; 594 REQUIRE_MESSAGE((it != newcont.end() && Value<T>::get(*it) == 1), "Element 1 has not been found properly"); 595 REQUIRE_MESSAGE((++it == range.second), "Range doesn't have the right number of elements"); 596 range = newcont.equal_range(2); 597 it = range.first; 598 REQUIRE_MESSAGE((it != newcont.end() && Value<T>::get(*it) == 2), "Element 2 has not been found properly"); 599 REQUIRE_MESSAGE((++it == range.second), "Range doesn't have the right number of elements"); 600 } 601 602 // T& operator=(const T& _Umap) 603 newcont = ccont; 604 REQUIRE_MESSAGE((AllowMultimapping<T>{} ? (newcont.size() == 3) : (newcont.size() == 2)), "Assignment operator has not copied the elements properly"); 605 606 607 cont.clear(); 608 CheckNoAllocations(cont); 609 for (int i = 0; i < 256; i++) 610 { 611 std::pair<typename T::iterator, bool> ins3 = cont.insert(Value<T>::make(i)); 612 REQUIRE_MESSAGE((ins3.second == true && Value<T>::get(*(ins3.first)) == i), "Element 1 has not been inserted properly"); 613 } 614 REQUIRE_MESSAGE(cont.size() == 256, "Wrong number of elements have been inserted"); 615 REQUIRE(!cont.range().empty()); 616 REQUIRE(!ccont.range().empty()); 617 REQUIRE((256 == CheckRecursiveRange<T,typename T::iterator>(cont.range()).first)); 618 REQUIRE((256 == CheckRecursiveRange<T,typename T::const_iterator>(ccont.range()).first)); 619 REQUIRE(cont.range().grainsize() > 0); 620 REQUIRE(ccont.range().grainsize() > 0); 621 622 // void swap(T&); 623 cont.swap(newcont); 624 REQUIRE_MESSAGE(newcont.size() == 256, "Wrong number of elements after swap"); 625 REQUIRE_MESSAGE(newcont.count(200) == 1, "Element with key 200 is not present after swap"); 626 REQUIRE_MESSAGE(newcont.count(16) == 1, "Element with key 16 is not present after swap"); 627 REQUIRE_MESSAGE(newcont.count(99) == 1, "Element with key 99 is not present after swap"); 628 REQUIRE_MESSAGE((AllowMultimapping<T>{} ? (cont.size() == 3) : (cont.size() == 2)), "Assignment operator has not copied the elements properly"); 629 630 T newcont_bkp = newcont; 631 newcont.swap(newcont); 632 REQUIRE_MESSAGE(newcont == newcont_bkp, "Unexpected swap-with-itself behavior"); 633 634 test_comparison_operators<T>(); 635 636 SpecialTests<T>::Test(); 637 } 638 639 template <typename Container> 640 class FillTable { 641 Container& my_table; 642 const int my_items; 643 bool my_asymptotic; 644 645 using pair_ib = std::pair<typename Container::iterator, bool>; 646 public: 647 FillTable(Container& table, int items, bool asymptotic) 648 : my_table(table), my_items(items), my_asymptotic(asymptotic) 649 { 650 REQUIRE((!(items&1) && items > 100)); 651 } 652 653 void operator()( int thread_index ) const { 654 if (thread_index == 0) { // Fill even keys forward (single thread) 655 bool last_inserted = true; 656 657 for (int i = 0; i < my_items; i += 2) { 658 int val = my_asymptotic ? 1 : i; 659 pair_ib pib = my_table.insert(Value<Container>::make(val)); 660 REQUIRE_MESSAGE((Value<Container>::get(*(pib.first)) == val), 661 "Element not properly inserted"); 662 REQUIRE_MESSAGE((last_inserted || !pib.second), 663 "Previous key was not inserted but current key is inserted"); 664 last_inserted = pib.second; 665 } 666 } else if (thread_index == 1) { // Fill even keys backward (single thread) 667 bool last_inserted = true; 668 669 for (int i = my_items - 2; i >= 0; i -= 2) { 670 int val = my_asymptotic ? 1 : i; 671 pair_ib pib = my_table.insert(Value<Container>::make(val)); 672 REQUIRE_MESSAGE((Value<Container>::get(*(pib.first)) == val), 673 "Element not properly inserted"); 674 REQUIRE_MESSAGE((last_inserted || !pib.second), 675 "Previous key was not inserted but current key is inserted"); 676 last_inserted = pib.second; 677 } 678 } else if (!(thread_index & 1)) { // Fill odd keys forward (multiple threads) 679 for (int i = 1; i < my_items; i += 2) { 680 if (i % 32 == 1 && i + 6 < my_items) { 681 if (my_asymptotic) { 682 auto init = { Value<Container>::make(1), Value<Container>::make(1), Value<Container>::make(1) }; 683 my_table.insert(init); 684 REQUIRE_MESSAGE(Value<Container>::get(*my_table.find(1)) == 1, "Element not properly inserted"); 685 } else { 686 auto init = { Value<Container>::make(i), Value<Container>::make(i + 2), 687 Value<Container>::make(i + 4) }; 688 my_table.insert(init); 689 REQUIRE_MESSAGE(Value<Container>::get(*my_table.find(i)) == i, "Element i not properly inserted"); 690 REQUIRE_MESSAGE(Value<Container>::get(*my_table.find(i + 2)) == i + 2, "Element i + 2 not properly inserted"); 691 REQUIRE_MESSAGE(Value<Container>::get(*my_table.find(i + 4)) == i + 4, "Element i + 4 not properly inserted"); 692 } 693 i += 4; 694 } else { 695 pair_ib pib = my_table.insert(Value<Container>::make(my_asymptotic ? 1 : i)); 696 REQUIRE_MESSAGE(Value<Container>::get(*(pib.first)) == (my_asymptotic ? 1 : i), "Element not properly inserted"); 697 } 698 } 699 } else { // Check odd keys backward (multiple threads) 700 if (!my_asymptotic) { 701 bool last_found = false; 702 for (int i = my_items - 1; i >= 0; i -= 2) { 703 typename Container::iterator it = my_table.find(i); 704 705 if (it != my_table.end()) { // found 706 REQUIRE_MESSAGE(Value<Container>::get(*it) == i, "Element not properly inserted"); 707 last_found = true; 708 } else { 709 REQUIRE_MESSAGE(!last_found, "Previous key was found, but current was not found"); 710 } 711 } 712 } 713 } 714 } 715 }; // class FillTable 716 717 template <typename Container, typename Range> 718 struct ParallelTraverseBody { 719 const int n; 720 atomic_byte_type* const array; 721 722 ParallelTraverseBody( atomic_byte_type arr[], int num ) 723 : n(num), array(arr) {} 724 725 void operator()( const Range& range ) const { 726 for (auto i = range.begin(); i != range.end(); ++i) { 727 int k = static_cast<int>(Value<Container>::key(*i)); 728 REQUIRE(k == Value<Container>::get(*i)); 729 REQUIRE(0 <= k); 730 REQUIRE(k < n); 731 ++array[k]; 732 } 733 } 734 }; // struct ParallelTraverseBody 735 736 template<typename T> 737 class CheckTable : utils::NoAssign { 738 T& table; 739 public: 740 CheckTable(T& t) : NoAssign(), table(t) {} 741 void operator()(int i) const { 742 int c = (int)table.count(i); 743 CHECK_MESSAGE(c, "must exist"); 744 } 745 }; 746 747 template <typename Container> 748 void test_concurrent_common( bool asymptotic = false ) { 749 #if __TBB_USE_ASSERT 750 int items = 2000; 751 #else 752 int items = 20000; 753 #endif 754 int items_inserted = 0; 755 int num_threads = 16; 756 757 Container table; 758 759 if (AllowMultimapping<Container>::value) { 760 // TODO: comment 761 items = 4 * items / (num_threads + 2); 762 items_inserted = items + (num_threads - 2) * items / 4; 763 } else { 764 items_inserted = items; 765 } 766 767 utils::NativeParallelFor(num_threads, FillTable<Container>(table, items, asymptotic)); 768 769 REQUIRE(int(table.size()) == items_inserted); 770 771 if (!asymptotic) { 772 atomic_byte_type* array = new atomic_byte_type[items]; 773 std::memset(static_cast<void*>(array), 0, items * sizeof(atomic_byte_type)); 774 775 typename Container::range_type r = table.range(); 776 777 auto p = CheckRecursiveRange<Container, typename Container::iterator>(r); 778 REQUIRE(items_inserted == p.first); 779 780 tbb::parallel_for(r, ParallelTraverseBody<Container, typename Container::range_type>(array, items)); 781 CheckRange(array, items, AllowMultimapping<Container>::value, (num_threads - 1)/2); 782 783 const Container& const_table = table; 784 std::memset(static_cast<void*>(array), 0, items * sizeof(atomic_byte_type)); 785 typename Container::const_range_type cr = const_table.range(); 786 787 p = CheckRecursiveRange<Container, typename Container::const_iterator>(cr); 788 REQUIRE(items_inserted == p.first); 789 790 tbb::parallel_for(cr, ParallelTraverseBody<Container, typename Container::const_range_type>(array, items)); 791 CheckRange(array, items, AllowMultimapping<Container>::value, (num_threads - 1) / 2); 792 delete[] array; 793 794 tbb::parallel_for(0, items, CheckTable<Container>(table)); 795 } 796 797 table.clear(); 798 // TODO: add check for container allocator counters 799 } 800 801 template <typename ContainerTraits> 802 void test_rvalue_ref_support() { 803 using namespace move_support_tests; 804 test_move_constructor<ContainerTraits>(); 805 test_move_assignment<ContainerTraits>(); 806 #if TBB_USE_EXCEPTIONS 807 test_ex_move_constructor<ContainerTraits>(); 808 #endif 809 } 810 811 template <typename Container> 812 void test_range_based_for_support() { 813 using namespace range_based_for_support_tests; 814 815 Container cont; 816 const int sequence_length = 100; 817 818 for (int i = 1; i <= sequence_length; ++i) { 819 cont.insert(Value<Container>::make(i)); 820 } 821 822 auto range_based_for_result = range_based_for_accumulate(cont, UnifiedSummer{}, 0); 823 auto reference_result = gauss_summ_of_int_sequence(sequence_length); 824 REQUIRE_MESSAGE(range_based_for_result == reference_result, 825 "Incorrect accumulated value generated via range based for"); 826 } 827 828 template <typename Container> 829 void test_initializer_list_support( std::initializer_list<typename Container::value_type> init ) { 830 using namespace initializer_list_support_tests; 831 832 test_initializer_list_support_without_assign<Container, TestInsertMethod>(init); 833 test_initializer_list_support_without_assign<Container, TestInsertMethod>({}); 834 } 835 836 template <typename Checker> 837 void test_set_specific_types() { 838 // TODO: add tests for atomics 839 Checker check_types; 840 const int num = 10; 841 842 // Test int 843 std::list<int> arr_int; 844 for (int i = 0; i != num; ++i) { 845 arr_int.emplace_back(i); 846 } 847 check_types.template check</*DefCtorPresent = */true>(arr_int); 848 849 // Test reference_wrapper 850 std::list<std::reference_wrapper<int>> arr_ref; 851 for (auto it = arr_int.begin(); it != arr_int.end(); ++it) { 852 arr_ref.emplace_back(*it); 853 } 854 check_types.template check</*DefCtorPresent = */false>(arr_ref); 855 856 // Test share_ptr 857 std::list<std::shared_ptr<int>> arr_shr; 858 for (int i = 0; i != num; ++i) { 859 arr_shr.emplace_back(std::make_shared<int>(i)); 860 } 861 check_types.template check</*DefCtorPresent = */true>(arr_shr); 862 863 // Test weak_ptr 864 std::list<std::weak_ptr<int>> arr_weak; 865 std::copy(arr_shr.begin(), arr_shr.end(), std::back_inserter(arr_weak)); 866 check_types.template check</*DefCtorPresent = */true>(arr_weak); 867 868 // Test std::pair 869 std::list<std::pair<int, int>> arr_pairs; 870 for (int i = 0; i != num; ++i) { 871 arr_pairs.emplace_back(i, i); 872 } 873 check_types.template check</*DefCtorPresent = */true>(arr_pairs); 874 875 // Test std::basic_string 876 std::list<std::basic_string<char, std::char_traits<char>, tbb::tbb_allocator<char>>> arr_strings; 877 for (int i = 0; i != num; ++i) { 878 arr_strings.emplace_back(i, char(i)); 879 } 880 check_types.template check</*DefCtorPresent = */true>(arr_strings); 881 } 882 883 template <typename Checker> 884 void test_map_specific_types() { 885 // TODO: add tests for int-atomic pairs 886 Checker check_types; 887 const int num = 10; 888 889 // Test int-int pairs 890 std::list<std::pair<const int, int>> arr_int_int_pairs; 891 for (int i = 0; i != num; ++i) { 892 arr_int_int_pairs.emplace_back(i, num - i); 893 } 894 check_types.template check</*DefCtorPresent = */true>(arr_int_int_pairs); 895 896 // Test reference_wrapper-int pairs 897 std::list<std::pair<const std::reference_wrapper<const int>, int>> arr_ref_int_pairs; 898 for (auto& item : arr_int_int_pairs) { 899 arr_ref_int_pairs.emplace_back(item.first, item.second); 900 } 901 check_types.template check</*DefCtorPresent = */true>(arr_ref_int_pairs); 902 903 // Test int-reference_wrapper pairs 904 std::list<std::pair<const int, std::reference_wrapper<int>>> arr_int_ref_pairs; 905 for (auto& item : arr_int_int_pairs) { 906 arr_int_ref_pairs.emplace_back(item.first, item.second); 907 } 908 check_types.template check</*DefCtorPresent = */false>(arr_int_ref_pairs); 909 910 // Test shared_ptr 911 std::list<std::pair<const std::shared_ptr<int>, std::shared_ptr<int>>> arr_shared_pairs; 912 for (int i = 0; i != num; ++i) { 913 const int num_minus_i = num - i; 914 arr_shared_pairs.emplace_back(std::make_shared<int>(i), std::make_shared<int>(num_minus_i)); 915 } 916 check_types.template check</*DefCtorPresent = */true>(arr_shared_pairs); 917 918 // Test weak_ptr 919 std::list<std::pair<const std::weak_ptr<int>, std::weak_ptr<int>>> arr_wick_pairs; 920 std::copy(arr_shared_pairs.begin(), arr_shared_pairs.end(), std::back_inserter(arr_wick_pairs)); 921 check_types.template check</*DefCtorPresent = */true>(arr_wick_pairs); 922 923 // Test std::pair 924 using pair_key_type = std::pair<int, int>; 925 std::list<std::pair<const pair_key_type, int>> arr_pair_int_pairs; 926 for (int i = 0; i != num; ++i) { 927 arr_pair_int_pairs.emplace_back(pair_key_type{i, i}, i); 928 } 929 check_types.template check</*DefCtorPresent = */true>(arr_pair_int_pairs); 930 931 // Test std::basic_string 932 using tbb_string_key_type = std::basic_string<char, std::char_traits<char>, tbb::tbb_allocator<char>>; 933 std::list<std::pair<const tbb_string_key_type, int>> arr_tbb_string_pairs; 934 for (int i = 0; i != num; ++i) { 935 tbb_string_key_type key(i, char(i)); 936 arr_tbb_string_pairs.emplace_back(key, i); 937 } 938 check_types.template check</*DefCtorPresent = */true>(arr_tbb_string_pairs); 939 } 940 941 namespace test { 942 943 // For the sake of simplified testing, make std::unique_ptr implicitly convertible to/from the pointer 944 template <typename T> 945 class unique_ptr : public std::unique_ptr<T> { 946 public: 947 using pointer = typename std::unique_ptr<T>::pointer; 948 949 unique_ptr( pointer p ) : std::unique_ptr<T>(p) {} 950 operator pointer() const { return this->get(); } 951 }; // class unique_ptr 952 953 } // namespace test 954 955 namespace std { 956 template <typename T> 957 struct hash<test::unique_ptr<T>> { 958 std::size_t operator()(const test::unique_ptr<T>& ptr) const { 959 return std::hash<std::unique_ptr<T>>{}(ptr); 960 } 961 }; 962 } 963 964 template <bool Condition> 965 struct CallIf { 966 template <typename Func> 967 void operator()( Func func ) const { func(); } 968 }; 969 970 template <> 971 struct CallIf<false> { 972 template <typename Func> 973 void operator()( Func ) const {} 974 }; 975 976 template <typename Table> 977 class TestOperatorSquareBrackets { 978 using value_type = typename Table::value_type; 979 Table& my_c; 980 const value_type& my_value; 981 public: 982 TestOperatorSquareBrackets( Table& c, const value_type& value ) 983 : my_c(c), my_value(value) {} 984 985 void operator()() const { 986 utils::IsEqual equal; 987 REQUIRE(equal(my_c[my_value.first], my_value.second)); 988 typename Table::key_type temp_key = my_value.first; 989 REQUIRE(equal(my_c[std::move(temp_key)], my_value.second)); 990 } 991 }; // class TestOperatorSquareBrackets 992 993 template <bool DefCtorPresent, typename Table, typename Value> 994 void TestSquareBracketsAndAt( Table&, const Value&, /*multimap = */std::true_type ) { 995 // operator [] and at are not presented in the multimap 996 } 997 998 template <bool DefCtorPresent, typename Table, typename Value> 999 void TestSquareBracketsAndAt( Table& c, const Value& value, /*multimap = */std::false_type ) { 1000 CallIf<DefCtorPresent>()(TestOperatorSquareBrackets<Table>(c, value)); 1001 utils::IsEqual equal; 1002 REQUIRE(equal(c.at(value.first), value.second)); 1003 // TODO: add test for at exceptions 1004 const Table& constC = c; 1005 REQUIRE(equal(constC.at(value.first), value.second)); 1006 } 1007 1008 template <bool DefCtorPresent, typename Table, typename Value> 1009 void TestMapSpecificMethods( Table&, const Value& ) {} 1010 1011 template <bool DefCtorPresent, typename Table> 1012 void TestMapSpecificMethods( Table& c, const std::pair<const typename Table::key_type, 1013 typename Table::mapped_type>& value ) 1014 { 1015 TestSquareBracketsAndAt<DefCtorPresent>(c, value, std::integral_constant<bool, AllowMultimapping<Table>::value>{}); 1016 } 1017 1018 template <bool DefCtorPresent, typename Table> 1019 class CheckValue { 1020 Table& my_c; 1021 public: 1022 CheckValue( Table& c ) : my_c(c) {} 1023 void operator()( const typename Table::value_type& value ) { 1024 using iterator = typename Table::iterator; 1025 using const_iterator = typename Table::const_iterator; 1026 const Table& constC = my_c; 1027 // count 1028 REQUIRE(my_c.count(Value<Table>::key(value)) == 1); 1029 // find 1030 utils::IsEqual equal; 1031 REQUIRE(equal(*my_c.find(Value<Table>::key(value)), value)); 1032 REQUIRE(equal(*constC.find(Value<Table>::key(value)), value)); 1033 // erase 1034 REQUIRE(my_c.unsafe_erase(Value<Table>::key(value)) != 0); 1035 REQUIRE(my_c.unsafe_erase(Value<Table>::key(value)) == 0); 1036 // insert 1037 std::pair<iterator, bool> res = my_c.insert(value); 1038 REQUIRE(equal(*res.first, value)); 1039 REQUIRE(res.second); 1040 // erase 1041 iterator it = res.first; 1042 ++it; 1043 REQUIRE(my_c.unsafe_erase(res.first) == it); 1044 // insert 1045 REQUIRE(equal(*my_c.insert(my_c.begin(), value), value)); 1046 // equal_range 1047 std::pair<iterator, iterator> r1 = my_c.equal_range(Value<Table>::key(value)); 1048 REQUIRE((equal(*r1.first, value) && ++r1.first == r1.second)); 1049 std::pair<const_iterator, const_iterator> r2 = constC.equal_range(Value<Table>::key(value)); 1050 REQUIRE((equal(*r2.first, value) && ++r2.first == r2.second)); 1051 1052 TestMapSpecificMethods<DefCtorPresent>(my_c, value); 1053 } 1054 }; // class CheckValue 1055 1056 namespace detail { 1057 1058 #if (__INTEL_COMPILER || __clang__ ) && __TBB_GLIBCXX_VERSION && __TBB_GLIBCXX_VERSION < 40900 1059 template <typename T> 1060 struct assignable_atomic : std::atomic<T> { 1061 using std::atomic<T>::operator=; 1062 assignable_atomic& operator=(const assignable_atomic& a) { 1063 store(a.load(std::memory_order_relaxed), std::memory_order_relaxed); 1064 } 1065 }; 1066 1067 template <typename T> 1068 using atomic_type = assignable_atomic<T>; 1069 #else 1070 template <typename T> 1071 using atomic_type = std::atomic<T>; 1072 #endif 1073 } 1074 1075 template <typename Value> 1076 class TestRange { 1077 const std::list<Value>& my_lst; 1078 std::vector<detail::atomic_type<bool>>& my_marks; 1079 public: 1080 TestRange( const std::list<Value>& lst, std::vector<detail::atomic_type<bool>>& marks ) 1081 : my_lst(lst), my_marks(marks) 1082 { 1083 std::fill(my_marks.begin(), my_marks.end(), false); 1084 } 1085 1086 template <typename Range> 1087 void operator()( const Range& r ) const { 1088 do_test_range(r.begin(), r.end()); 1089 } 1090 1091 template <typename Iterator> 1092 void do_test_range( Iterator i, Iterator j ) const { 1093 for (Iterator it = i; it != j;) { 1094 Iterator prev_it = it++; 1095 auto it2 = std::search(my_lst.begin(), my_lst.end(), prev_it, it, utils::IsEqual()); 1096 REQUIRE(it2 != my_lst.end()); 1097 auto dist = std::distance(my_lst.begin(), it2); 1098 REQUIRE(!my_marks[dist]); 1099 my_marks[dist] = true; 1100 } 1101 } 1102 }; // class TestRange 1103 1104 template <bool DefCtorPresent, typename Table> 1105 void CommonExamine( Table c, const std::list<typename Table::value_type>& lst ) { 1106 using value_type = typename Table::value_type; 1107 1108 if (!(!c.empty() && c.size() == lst.size() && c.max_size() >= c.size())) { 1109 std::cout << std::boolalpha; 1110 std::cout << "Empty? " << c.empty() << std::endl; 1111 std::cout << "sizes equal? " << (c.size() == lst.size()) << std::endl; 1112 std::cout << "\t" << c.size() << std::endl; 1113 std::cout << "\t" << lst.size() << std::endl; 1114 std::cout << "Max size greater? " << (c.max_size() >= c.size()) << std::endl; 1115 } 1116 REQUIRE((!c.empty() && c.size() == lst.size() && c.max_size() >= c.size())); 1117 1118 std::for_each(lst.begin(), lst.end(), CheckValue<DefCtorPresent, Table>(c)); 1119 1120 std::vector<detail::atomic_type<bool>> marks(lst.size()); 1121 1122 TestRange<value_type>(lst, marks).do_test_range(c.begin(), c.end()); 1123 REQUIRE(std::find(marks.begin(), marks.end(), false) == marks.end()); 1124 1125 const Table constC = c; 1126 REQUIRE(c.size() == constC.size()); 1127 1128 TestRange<value_type>(lst, marks).do_test_range(c.begin(), c.end()); 1129 REQUIRE(std::find(marks.begin(), marks.end(), false) == marks.end()); 1130 1131 tbb::parallel_for(c.range(), TestRange<value_type>(lst, marks)); 1132 REQUIRE(std::find(marks.begin(), marks.end(), false) == marks.end()); 1133 1134 tbb::parallel_for(constC.range(), TestRange<value_type>(lst, marks)); 1135 REQUIRE(std::find(marks.begin(), marks.end(), false) == marks.end()); 1136 1137 Table c2; 1138 auto begin5 = lst.begin(); 1139 std::advance(begin5, 5); 1140 c2.insert(lst.begin(), begin5); 1141 std::for_each(lst.begin(), begin5, CheckValue<DefCtorPresent, Table>(c2)); 1142 1143 c2.swap(c); 1144 REQUIRE(c2.size() == lst.size()); 1145 REQUIRE(c.size() == 5); 1146 1147 std::for_each(lst.begin(), lst.end(), CheckValue<DefCtorPresent, Table>(c2)); 1148 1149 c2.clear(); 1150 REQUIRE(c2.size() == 0); 1151 1152 auto alloc = c.get_allocator(); 1153 value_type* ptr = alloc.allocate(1); 1154 REQUIRE(ptr != nullptr); 1155 alloc.deallocate(ptr, 1); 1156 } 1157 1158 template <typename ContainerTraits> 1159 void test_scoped_allocator() { 1160 using allocator_data_type = AllocatorAwareData<std::scoped_allocator_adaptor<std::allocator<int>>>; 1161 using basic_allocator_type = std::scoped_allocator_adaptor<std::allocator<allocator_data_type>>; 1162 using container_value_type = typename ContainerTraits::template container_value_type<allocator_data_type>; 1163 using allocator_type = typename std::allocator_traits<basic_allocator_type>::template rebind_alloc<container_value_type>; 1164 using container_type = typename ContainerTraits::template container_type<allocator_data_type, allocator_type>; 1165 1166 allocator_type allocator; 1167 allocator_data_type key1(1, allocator); 1168 allocator_data_type key2(2, allocator); 1169 1170 container_value_type value1 = Value<container_type>::make(key1); 1171 container_value_type value2 = Value<container_type>::make(key2); 1172 1173 auto init_list = { value1, value2 }; 1174 1175 container_type c1(allocator); 1176 container_type c2(allocator); 1177 1178 allocator_data_type::activate(); 1179 1180 emplace_helpers::call_emplace(c1, key1); 1181 emplace_helpers::call_emplace(c2, std::move(key2)); 1182 1183 c1.clear(); 1184 c2.clear(); 1185 1186 c1.insert(value1); 1187 c2.insert(std::move(value2)); 1188 1189 c1.clear(); 1190 c2.clear(); 1191 1192 c1.insert(init_list); 1193 c2.insert(value1); 1194 1195 c1 = c2; 1196 c2 = std::move(c1); 1197 1198 allocator_data_type::deactivate(); 1199 } 1200 1201 1202 struct int_key { 1203 int my_item; 1204 int_key(int i) : my_item(i) {} 1205 }; 1206 1207 bool operator==(const int_key& ik, int i) { return ik.my_item == i; } 1208 bool operator==(int i, const int_key& ik) { return i == ik.my_item; } 1209 bool operator==(const int_key& ik1, const int_key& ik2) { return ik1.my_item == ik2.my_item; } 1210 1211 bool operator<( const int_key& ik, int i ) { return ik.my_item < i; } 1212 bool operator<( int i, const int_key& ik ) { return i < ik.my_item; } 1213 bool operator<( const int_key& ik1, const int_key& ik2 ) { return ik1.my_item < ik2.my_item; } 1214 1215 struct char_key { 1216 const char* my_item; 1217 char_key(const char* c) : my_item(c) {} 1218 1219 const char& operator[] (std::size_t pos) const { 1220 return my_item[pos]; 1221 } 1222 1223 std::size_t size() const { 1224 return std::strlen(my_item); 1225 } 1226 }; 1227 1228 bool operator==(const char_key& ck, std::string c) { 1229 std::size_t i = 0; 1230 while (ck[i] != '\0' && i < c.size() && ck[i] == c[i]) { ++i;} 1231 return c.size() == i && ck[i] == '\0'; 1232 } 1233 bool operator==(std::string c, const char_key& ck) { 1234 return ck == c; 1235 } 1236 bool operator==(const char_key& ck1, const char_key& ck2) { 1237 std::size_t i = 0; 1238 while (ck1[i] != '\0' && ck2[i] != '\0' && ck1[i] == ck2[i]) { ++i;} 1239 return ck1[i] == ck2[i]; 1240 } 1241 1242 bool operator<( const char_key& ck, std::string c ) { 1243 return std::lexicographical_compare(ck.my_item, ck.my_item + ck.size(), c.begin(), c.end()); 1244 } 1245 1246 bool operator<(std::string c, const char_key& ck) { 1247 return std::lexicographical_compare(c.begin(), c.end(), ck.my_item, ck.my_item + ck.size()); 1248 } 1249 1250 bool operator<( const char_key& ck1, const char_key& ck2 ) { 1251 return std::lexicographical_compare(ck1.my_item, ck1.my_item + ck1.size(), ck2.my_item, ck2.my_item + ck2.size()); 1252 } 1253 1254 struct equal_to { 1255 using is_transparent = int; 1256 template <typename T, typename W> 1257 bool operator()(const T &lhs, const W &rhs) const { 1258 return lhs == rhs; 1259 } 1260 }; 1261 1262 struct hash_with_transparent_key_equal { 1263 template <typename T> 1264 size_t operator()(const T& key) const { return hash(key); } 1265 using transparent_key_equal = equal_to; 1266 int prime = 433494437; 1267 int first_factor = 41241245; 1268 int second_factor = 2523422; 1269 1270 size_t hash(const int& key) const { return (first_factor * key + second_factor) % prime; } 1271 1272 size_t hash(const int_key& key) const { return (first_factor * key.my_item + second_factor) % prime; } 1273 1274 size_t hash(const std::string& key) const { 1275 int sum = 0; 1276 for (auto it = key.begin(); it != key.end(); ++it) { 1277 sum += first_factor * (*it) + second_factor; 1278 } 1279 return sum % prime; 1280 } 1281 1282 size_t hash(const char_key& key) const { 1283 int sum = 0; 1284 int i = 0; 1285 while (key[i] != '\0') { 1286 sum += first_factor * key[i++] + second_factor; 1287 } 1288 return sum % prime; 1289 } 1290 1291 }; 1292 1293 template <typename Container> 1294 void check_heterogeneous_functions_key_int_impl() { 1295 static_assert(std::is_same<typename Container::key_type, int>::value, 1296 "incorrect key_type for heterogeneous lookup test"); 1297 // Initialization 1298 Container c; 1299 int size = 10; 1300 for (int i = 0; i < size; i++) { 1301 c.insert(Value<Container>::make(i)); 1302 } 1303 // Insert first duplicated element for multicontainers 1304 if (AllowMultimapping<Container>::value) { 1305 c.insert(Value<Container>::make(0)); 1306 } 1307 1308 // Look up testing 1309 for (int i = 0; i < size; i++) { 1310 int_key k(i); 1311 int key = i; 1312 REQUIRE_MESSAGE(c.find(k) == c.find(key), "Incorrect heterogeneous find return value"); 1313 REQUIRE_MESSAGE(c.count(k) == c.count(key), "Incorrect heterogeneous count return value"); 1314 } 1315 1316 // unsafe_extract testing 1317 for (int i = 0; i < size; i++) { 1318 Container extract_c = c; 1319 int_key int_k(i); 1320 auto int_key_extract = extract_c.unsafe_extract(int_k); 1321 if (!AllowMultimapping<Container>::value) { 1322 REQUIRE_MESSAGE(extract_c.find(int_k) == extract_c.end(), "Key exists after extract"); 1323 } 1324 REQUIRE_MESSAGE(!int_key_extract.empty(), "Empty node with exists key"); 1325 REQUIRE_MESSAGE(node_handling_tests::compare_handle_getters(int_key_extract, Value<Container>::make(i)), "Incorrect node"); 1326 } 1327 1328 // unsafe_extract not exists key 1329 auto not_exists = c.unsafe_extract(int_key(100)); 1330 REQUIRE_MESSAGE(not_exists.empty(), "Not empty node with not exists key"); 1331 1332 // multimap unsafe_extract testing 1333 if (AllowMultimapping<Container>::value) { 1334 Container extract_m; 1335 for (int i = 0; i < size; i++) { 1336 extract_m.insert(Value<Container>::make(i)); 1337 extract_m.insert(Value<Container>::make(i, i + 1)); 1338 } 1339 for (int i = 0; i < size; i++) { 1340 int_key int_k(i); 1341 auto int_key_extract = extract_m.unsafe_extract(int_k); 1342 REQUIRE_MESSAGE(!int_key_extract.empty(), "Empty node with exists key"); 1343 REQUIRE_MESSAGE((node_handling_tests::compare_handle_getters(int_key_extract, Value<Container>::make(i, i)) || 1344 node_handling_tests::compare_handle_getters(int_key_extract, Value<Container>::make(i, i + 1))), "Incorrect node"); 1345 REQUIRE_MESSAGE(extract_m.find(int_k) != extract_m.end(), "All nodes for key deleted"); 1346 } 1347 } 1348 1349 // Erase testing 1350 for (int i = 0; i < size; i++) { 1351 auto count_before_erase = c.count(i); 1352 auto result = c.unsafe_erase(int_key(i)); 1353 REQUIRE_MESSAGE(count_before_erase == result,"Incorrect erased elements count"); 1354 REQUIRE_MESSAGE(c.count(i) == 0, "Some elements was not erased"); 1355 } 1356 1357 } 1358 1359 template <typename Container> 1360 void check_heterogeneous_functions_key_string_impl() { 1361 static_assert(std::is_same<typename Container::key_type, std::string>::value, 1362 "incorrect key_type for heterogeneous lookup test"); 1363 // Initialization 1364 std::vector<const char*> keys{"key1", "key2", "key3", "key4", 1365 "key5", "key6", "key7", "key8", "key9", "key10"}; 1366 std::vector<const char*> values{"value1", "value2", "value3", "value4", 1367 "value5", "value6", "value7", "value8", "value9", "value10", "value11"}; 1368 Container c; 1369 for (auto it = keys.begin(); it != keys.end(); ++it) { 1370 c.insert(Value<Container>::make(*it)); 1371 } 1372 // Insert first duplicated element for multicontainers 1373 if (AllowMultimapping<Container>::value) { 1374 c.insert(Value<Container>::make(*keys.begin())); 1375 } 1376 1377 // Look up testing 1378 for (auto it = keys.begin(); it != keys.end(); ++it) { 1379 std::string key = *it; 1380 char_key k{*it}; 1381 REQUIRE_MESSAGE(c.find(k) == c.find(key), "Incorrect heterogeneous find return value"); 1382 REQUIRE_MESSAGE(c.count(k) == c.count(key), "Incorrect heterogeneous count return value"); 1383 } 1384 1385 // unsafe_extract testing 1386 for (auto it = keys.begin(); it != keys.end(); ++it) { 1387 Container extract_c = c; 1388 char_key k{*it}; 1389 auto char_key_extract = extract_c.unsafe_extract(k); 1390 REQUIRE_MESSAGE(!char_key_extract.empty(), "Empty node with exists key"); 1391 REQUIRE_MESSAGE(node_handling_tests::compare_handle_getters(char_key_extract, Value<Container>::make(*it)), "Incorrect node"); 1392 } 1393 1394 // unsafe_extract not exists key 1395 auto not_exists = c.unsafe_extract(char_key("not exists")); 1396 REQUIRE_MESSAGE(not_exists.empty(), "Not empty node with not exists key"); 1397 1398 // multimap unsafe_extract testing 1399 if (AllowMultimapping<Container>::value){ 1400 Container extract_m; 1401 for (std::size_t i = 0; i < keys.size(); i++) { 1402 extract_m.insert(Value<Container>::make(keys[i], values[i])); 1403 extract_m.insert(Value<Container>::make(keys[i], values[i + 1])); 1404 } 1405 for (std::size_t i = 0; i < keys.size(); i++) { 1406 char_key char_k(keys[i]); 1407 auto char_key_extract = extract_m.unsafe_extract(char_k); 1408 REQUIRE_MESSAGE(!char_key_extract.empty(), "Empty node with exists key"); 1409 REQUIRE_MESSAGE((node_handling_tests::compare_handle_getters(char_key_extract, Value<Container>::make(keys[i], values[i])) || 1410 node_handling_tests::compare_handle_getters(char_key_extract, Value<Container>::make(keys[i], values[i + 1]))), "Incorrect node"); 1411 REQUIRE_MESSAGE(extract_m.find(char_k) != extract_m.end(), "All nodes for key deleted"); 1412 } 1413 } 1414 1415 // Erase testing 1416 for (auto it = keys.begin(); it != keys.end(); ++it) { 1417 std::string key = *it; 1418 char_key k{*it}; 1419 auto count_before_erase = c.count(key); 1420 auto result = c.unsafe_erase(k); 1421 REQUIRE_MESSAGE(count_before_erase==result,"Incorrect erased elements count"); 1422 REQUIRE_MESSAGE(c.count(key)==0, "Some elements was not erased"); 1423 } 1424 } 1425 1426 struct CountingKey { 1427 static std::size_t counter; 1428 1429 CountingKey() { ++counter; } 1430 CountingKey( const CountingKey& ) { ++counter; } 1431 ~CountingKey() {} 1432 CountingKey& operator=( const CountingKey& ) { return *this; } 1433 1434 static void reset() { counter = 0; } 1435 }; 1436 1437 std::size_t CountingKey::counter; 1438 1439 namespace std { 1440 template <> 1441 struct hash<CountingKey> { 1442 std::size_t operator()( const CountingKey& ) const { return 0; } 1443 }; 1444 } 1445 1446 bool operator==( const CountingKey&, const CountingKey& ) { return true; } 1447 1448 bool operator<( const CountingKey&, const CountingKey& ) { return true; } 1449 1450 struct int_constructible_object { 1451 int_constructible_object(int k) : key(k) {} 1452 int key; 1453 }; // struct int_constructible_object 1454 1455 bool operator==( const int_constructible_object& lhs, const int_constructible_object rhs ) { 1456 return lhs.key == rhs.key; 1457 } 1458 1459 // A test for 1460 // template <typename P> insert(P&&) in maps 1461 template <template <typename...> class Container> 1462 void test_insert_by_generic_pair() { 1463 using value_type = std::pair<const int, int_constructible_object>; 1464 using generic_pair_type = std::pair<int, int>; 1465 1466 static_assert(std::is_constructible<value_type, generic_pair_type>::value, 1467 "Incorrect test setup"); 1468 1469 Container<int, int_constructible_object> cont1, cont2; 1470 using iterator = typename Container<int, int_constructible_object>::iterator; 1471 1472 for (int i = 0; i != 10; ++i) { 1473 std::pair<iterator, bool> res_generic_insert = cont1.insert(generic_pair_type(1, i)); 1474 std::pair<iterator, bool> res_value_insert = cont2.insert(value_type(1, int_constructible_object(i))); 1475 1476 REQUIRE_MESSAGE(*res_generic_insert.first == *res_value_insert.first, "Insert by generic pair returned wrong iterator"); 1477 REQUIRE_MESSAGE(res_generic_insert.second == res_value_insert.second, "Insert by generic pair returned wrong insertion value"); 1478 } 1479 1480 for (int i = 0; i != 10; ++i) { 1481 iterator res_generic_insert_hint = cont1.insert(cont1.cbegin(), generic_pair_type(2, i)); 1482 iterator res_value_insert_hint = cont2.insert(cont2.cbegin(), value_type(2, int_constructible_object(i))); 1483 1484 REQUIRE_MESSAGE(*res_generic_insert_hint == *res_value_insert_hint, "Hinted insert by generic pair returned wrong iterator"); 1485 } 1486 1487 Container<CountingKey, int_constructible_object> counting_cont; 1488 using counting_generic_pair = std::pair<CountingKey, int>; 1489 1490 static_assert(std::is_constructible<typename decltype(counting_cont)::value_type, counting_generic_pair>::value, 1491 "Incorrect test setup"); 1492 1493 counting_generic_pair counting_pair(CountingKey{}, 1); 1494 CountingKey::reset(); 1495 1496 counting_cont.insert(counting_pair); 1497 REQUIRE_MESSAGE(CountingKey::counter == 1, "Only one element should be constructed in-place during the generic pair insertion"); 1498 1499 CountingKey::reset(); 1500 } 1501 1502 template <typename Container> 1503 void test_swap_not_always_equal_allocator() { 1504 static_assert(std::is_same<typename Container::allocator_type, NotAlwaysEqualAllocator<typename Container::value_type>>::value, 1505 "Incorrect allocator in not always equal test"); 1506 Container c1{}; 1507 Container c2{Value<Container>::make(1), Value<Container>::make(2)}; 1508 1509 Container c1_copy = c1; 1510 Container c2_copy = c2; 1511 1512 c1.swap(c2); 1513 1514 REQUIRE_MESSAGE(c1 == c2_copy, "Incorrect swap with not always equal allocator"); 1515 REQUIRE_MESSAGE(c2 == c1_copy, "Incorrect swap with not always equal allocator"); 1516 } 1517 1518 #if TBB_USE_EXCEPTIONS 1519 template <typename Container> 1520 void test_exception_on_copy_ctor() { 1521 Container c1; 1522 c1.emplace(Value<Container>::make(ThrowOnCopy{})); 1523 1524 using container_allocator_type = std::allocator<Container>; 1525 using alloc_traits = std::allocator_traits<container_allocator_type>; 1526 container_allocator_type container_allocator; 1527 Container* c2_ptr = alloc_traits::allocate(container_allocator, 1); 1528 1529 ThrowOnCopy::activate(); 1530 // Test copy ctor 1531 try { 1532 alloc_traits::construct(container_allocator, c2_ptr, c1); 1533 } catch ( int error_code ) { 1534 REQUIRE_MESSAGE(error_code == ThrowOnCopy::error_code(), "Incorrect code was thrown"); 1535 } 1536 1537 REQUIRE_MESSAGE(c2_ptr->empty(), "Incorrect container state after throwing copy constructor"); 1538 1539 alloc_traits::deallocate(container_allocator, c2_ptr, 1); 1540 c2_ptr = alloc_traits::allocate(container_allocator, 1); 1541 1542 // Test copy ctor with allocator 1543 try { 1544 auto value_allocator = c1.get_allocator(); 1545 alloc_traits::construct(container_allocator, c2_ptr, c1, value_allocator); 1546 } catch( int error_code ) { 1547 REQUIRE_MESSAGE(error_code == ThrowOnCopy::error_code(), "Incorrect code was thrown"); 1548 } 1549 1550 REQUIRE_MESSAGE(c2_ptr->empty(), "Incorrect container state after throwing copy ctor with allocator"); 1551 alloc_traits::deallocate(container_allocator, c2_ptr, 1); 1552 ThrowOnCopy::deactivate(); 1553 } 1554 #endif // TBB_USE_EXCEPTIONS 1555 1556 #endif // __TBB_test_common_concurrent_associative_common_H 1557