1 /* 2 Copyright (c) 2005-2023 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; makeValueFactoryBase59 static Value make( const Key& key ) { return Value(key, key); } makeValueFactoryBase60 static Value make( const Key& key, const Key& mapped ) { return Value(key, mapped); } keyValueFactoryBase61 static strip_key key( const Value& value ) { return value.first; } getValueFactoryBase62 static strip_key get( const Value& value ) { return strip_key(value.second); } 63 template <typename U> convertValueFactoryBase64 static U convert( const Value& value ) { return U(value.second); } 65 }; 66 67 template <typename T> 68 struct SpecialTests { TestSpecialTests69 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 should 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 Range, typename Container> 429 void test_empty_container_range(Container&& cont) { 430 REQUIRE(cont.empty()); 431 Range r = cont.range(); 432 REQUIRE_MESSAGE(r.empty(), "Empty container range should be empty"); 433 REQUIRE_MESSAGE(!r.is_divisible(), "Empty container range should not be divisible"); 434 REQUIRE_MESSAGE(r.begin() == r.end(), "Incorrect iterators on empty range"); 435 REQUIRE_MESSAGE(r.begin() == cont.begin(), "Incorrect iterators on empty range"); 436 } 437 438 template<typename T, typename CheckElementState> 439 void test_basic_common() 440 { 441 T cont; 442 const T &ccont(cont); 443 CheckNoAllocations(cont); 444 // bool empty() const; 445 REQUIRE_MESSAGE(ccont.empty(), "Concurrent container is not empty after construction"); 446 447 // size_type size() const; 448 REQUIRE_MESSAGE(ccont.size() == 0, "Concurrent container is not empty after construction"); 449 450 // size_type max_size() const; 451 REQUIRE_MESSAGE(ccont.max_size() > 0, "Concurrent container max size is invalid"); 452 453 //iterator begin(); 454 //iterator end(); 455 REQUIRE_MESSAGE(cont.begin() == cont.end(), "Concurrent container iterators are invalid after construction"); 456 REQUIRE_MESSAGE(ccont.begin() == ccont.end(), "Concurrent container iterators are invalid after construction"); 457 REQUIRE_MESSAGE(cont.cbegin() == cont.cend(), "Concurrent container iterators are invalid after construction"); 458 459 // Test range for empty container 460 using range_type = typename T::range_type; 461 using const_range_type = typename T::const_range_type; 462 test_empty_container_range<range_type>(cont); 463 test_empty_container_range<const_range_type>(cont); 464 test_empty_container_range<const_range_type>(ccont); 465 466 T empty_cont; 467 const T& empty_ccont = empty_cont; 468 469 for (int i = 0; i < 1000; ++i) { 470 empty_cont.insert(Value<T>::make(i)); 471 } 472 empty_cont.clear(); 473 474 test_empty_container_range<range_type>(empty_cont); 475 test_empty_container_range<const_range_type>(empty_cont); 476 test_empty_container_range<const_range_type>(empty_ccont); 477 478 //std::pair<iterator, bool> insert(const value_type& obj); 479 std::pair<typename T::iterator, bool> ins = cont.insert(Value<T>::make(1)); 480 REQUIRE_MESSAGE((ins.second == true && Value<T>::get(*(ins.first)) == 1), "Element 1 has not been inserted properly"); 481 482 test_rvalue_insert<T,CheckElementState>(1,2); 483 test_emplace_insert<T,CheckElementState>(1,2); 484 485 // bool empty() const; 486 REQUIRE_MESSAGE(!ccont.empty(), "Concurrent container is empty after adding an element"); 487 488 // size_type size() const; 489 REQUIRE_MESSAGE(ccont.size() == 1, "Concurrent container size is incorrect"); 490 491 std::pair<typename T::iterator, bool> ins2 = cont.insert(Value<T>::make(1)); 492 493 if (AllowMultimapping<T>::value) 494 { 495 // std::pair<iterator, bool> insert(const value_type& obj); 496 REQUIRE_MESSAGE((ins2.second == true && Value<T>::get(*(ins2.first)) == 1), "Element 1 has not been inserted properly"); 497 498 // size_type size() const; 499 REQUIRE_MESSAGE(ccont.size() == 2, "Concurrent container size is incorrect"); 500 501 // size_type count(const key_type& k) const; 502 REQUIRE_MESSAGE(ccont.count(1) == 2, "Concurrent container count(1) is incorrect"); 503 // std::pair<iterator, iterator> equal_range(const key_type& k); 504 std::pair<typename T::iterator, typename T::iterator> range = cont.equal_range(1); 505 typename T::iterator it; 506 it = range.first; 507 REQUIRE_MESSAGE((it != cont.end() && Value<T>::get(*it) == 1), "Element 1 has not been found properly"); 508 unsigned int count = 0; 509 for (; it != range.second; it++) 510 { 511 count++; 512 REQUIRE_MESSAGE((Value<T>::get(*it) == 1), "Element 1 has not been found properly"); 513 } 514 515 REQUIRE_MESSAGE(count == 2, "Range doesn't have the right number of elements"); 516 } 517 else 518 { 519 // std::pair<iterator, bool> insert(const value_type& obj); 520 REQUIRE_MESSAGE((ins2.second == false && ins2.first == ins.first), "Element 1 should not be re-inserted"); 521 522 // size_type size() const; 523 REQUIRE_MESSAGE(ccont.size() == 1, "Concurrent container size is incorrect"); 524 525 // size_type count(const key_type& k) const; 526 REQUIRE_MESSAGE(ccont.count(1) == 1, "Concurrent container count(1) is incorrect"); 527 528 // std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const; 529 // std::pair<iterator, iterator> equal_range(const key_type& k); 530 std::pair<typename T::iterator, typename T::iterator> range = cont.equal_range(1); 531 typename T::iterator it = range.first; 532 REQUIRE_MESSAGE((it != cont.end() && Value<T>::get(*it) == 1), "Element 1 has not been found properly"); 533 REQUIRE_MESSAGE((++it == range.second), "Range doesn't have the right number of elements"); 534 } 535 536 // const_iterator find(const key_type& k) const; 537 // iterator find(const key_type& k); 538 typename T::iterator it = cont.find(1); 539 REQUIRE_MESSAGE((it != cont.end() && Value<T>::get(*(it)) == 1), "Element 1 has not been found properly"); 540 REQUIRE_MESSAGE(ccont.find(1) == it, "Element 1 has not been found properly"); 541 542 //bool contains(const key_type&k) const 543 REQUIRE_MESSAGE(cont.contains(1), "contains() cannot detect existing element"); 544 REQUIRE_MESSAGE(!cont.contains(0), "contains() detect not existing element"); 545 546 // iterator insert(const_iterator hint, const value_type& obj); 547 typename T::iterator it2 = cont.insert(ins.first, Value<T>::make(2)); 548 REQUIRE_MESSAGE((Value<T>::get(*it2) == 2), "Element 2 has not been inserted properly"); 549 550 // T(const T& _Umap) 551 T newcont = ccont; 552 553 REQUIRE_MESSAGE((AllowMultimapping<T>{} ? (newcont.size() == 3) : (newcont.size() == 2)), "Copy construction has not copied the elements properly"); 554 555 // size_type unsafe_erase(const key_type& k); 556 typename T::size_type size; 557 #if _MSC_VER && __INTEL_COMPILER == 1900 558 // The compiler optimizes the next line too aggressively. 559 #pragma noinline 560 #endif 561 size = cont.unsafe_erase(1); 562 563 REQUIRE_MESSAGE((AllowMultimapping<T>{} ? (size == 2) : (size == 1)), "Erase has not removed the right number of elements"); 564 565 // iterator unsafe_erase(iterator position); 566 typename T::iterator it4 = cont.unsafe_erase(cont.find(2)); 567 568 REQUIRE_MESSAGE((it4 == cont.end() && cont.size() == 0), "Erase has not removed the last element properly"); 569 570 // iterator unsafe_erase(const_iterator position); 571 cont.insert(Value<T>::make(3)); 572 typename T::iterator it5 = cont.unsafe_erase(cont.cbegin()); 573 REQUIRE_MESSAGE((it5 == cont.end() && cont.size() == 0), "Erase has not removed the last element properly"); 574 575 // template<class InputIterator> void insert(InputIterator first, InputIterator last); 576 577 cont.insert(newcont.begin(), newcont.end()); 578 579 REQUIRE_MESSAGE((AllowMultimapping<T>{} ? (cont.size() == 3) : (cont.size() == 2)), "Range insert has not copied the elements properly"); 580 581 // iterator unsafe_erase(const_iterator first, const_iterator last); 582 std::pair<typename T::iterator, typename T::iterator> range2 = newcont.equal_range(1); 583 584 newcont.unsafe_erase(range2.first, range2.second); 585 REQUIRE_MESSAGE(newcont.size() == 1, "Range erase has not erased the elements properly"); 586 587 // void clear(); 588 newcont.clear(); 589 REQUIRE_MESSAGE((newcont.begin() == newcont.end() && newcont.size() == 0), "Clear has not cleared the container"); 590 591 // void insert(std::initializer_list<value_type> &il); 592 newcont.insert( { Value<T>::make( 1 ), Value<T>::make( 2 ), Value<T>::make( 1 ) } ); 593 if (AllowMultimapping<T>::value) { 594 595 REQUIRE_MESSAGE(newcont.size() == 3, "Concurrent container size is incorrect"); 596 REQUIRE_MESSAGE(newcont.count(1) == 2, "Concurrent container count(1) is incorrect"); 597 REQUIRE_MESSAGE(newcont.count(2) == 1, "Concurrent container count(2) is incorrect"); 598 std::pair<typename T::iterator, typename T::iterator> range = cont.equal_range(1); 599 it = range.first; 600 // REQUIRE_MESSAGE((it != newcont.end() && Value<T>::get(*it) == 1), "Element 1 has not been found properly"); 601 REQUIRE_MESSAGE((it != newcont.end()), "iterator" ); 602 REQUIRE_MESSAGE((Value<T>::get(*it) == 1), "value"); 603 unsigned int count = 0; 604 for (; it != range.second; it++) { 605 count++; 606 REQUIRE_MESSAGE(Value<T>::get(*it) == 1, "Element 1 has not been found properly"); 607 } 608 REQUIRE_MESSAGE(count == 2, "Range doesn't have the right number of elements"); 609 range = newcont.equal_range(2); it = range.first; 610 REQUIRE_MESSAGE((it != newcont.end() && Value<T>::get(*it) == 2), "Element 2 has not been found properly"); 611 count = 0; 612 for (; it != range.second; it++) { 613 count++; 614 REQUIRE_MESSAGE(Value<T>::get(*it) == 2, "Element 2 has not been found properly"); 615 } 616 REQUIRE_MESSAGE(count == 1, "Range doesn't have the right number of elements"); 617 } else { 618 REQUIRE_MESSAGE(newcont.size() == 2, "Concurrent container size is incorrect"); 619 REQUIRE_MESSAGE(newcont.count(1) == 1, "Concurrent container count(1) is incorrect"); 620 REQUIRE_MESSAGE(newcont.count(2) == 1, "Concurrent container count(2) is incorrect"); 621 std::pair<typename T::iterator, typename T::iterator> range = newcont.equal_range(1); 622 it = range.first; 623 REQUIRE_MESSAGE((it != newcont.end() && Value<T>::get(*it) == 1), "Element 1 has not been found properly"); 624 REQUIRE_MESSAGE((++it == range.second), "Range doesn't have the right number of elements"); 625 range = newcont.equal_range(2); 626 it = range.first; 627 REQUIRE_MESSAGE((it != newcont.end() && Value<T>::get(*it) == 2), "Element 2 has not been found properly"); 628 REQUIRE_MESSAGE((++it == range.second), "Range doesn't have the right number of elements"); 629 } 630 631 // T& operator=(const T& _Umap) 632 newcont = ccont; 633 REQUIRE_MESSAGE((AllowMultimapping<T>{} ? (newcont.size() == 3) : (newcont.size() == 2)), "Assignment operator has not copied the elements properly"); 634 635 636 cont.clear(); 637 CheckNoAllocations(cont); 638 for (int i = 0; i < 256; i++) 639 { 640 std::pair<typename T::iterator, bool> ins3 = cont.insert(Value<T>::make(i)); 641 REQUIRE_MESSAGE((ins3.second == true && Value<T>::get(*(ins3.first)) == i), "Element 1 has not been inserted properly"); 642 } 643 REQUIRE_MESSAGE(cont.size() == 256, "Wrong number of elements have been inserted"); 644 REQUIRE(!cont.range().empty()); 645 REQUIRE(!ccont.range().empty()); 646 REQUIRE((256 == CheckRecursiveRange<T,typename T::iterator>(cont.range()).first)); 647 REQUIRE((256 == CheckRecursiveRange<T,typename T::const_iterator>(ccont.range()).first)); 648 REQUIRE(cont.range().grainsize() > 0); 649 REQUIRE(ccont.range().grainsize() > 0); 650 651 // void swap(T&); 652 cont.swap(newcont); 653 REQUIRE_MESSAGE(newcont.size() == 256, "Wrong number of elements after swap"); 654 REQUIRE_MESSAGE(newcont.count(200) == 1, "Element with key 200 is not present after swap"); 655 REQUIRE_MESSAGE(newcont.count(16) == 1, "Element with key 16 is not present after swap"); 656 REQUIRE_MESSAGE(newcont.count(99) == 1, "Element with key 99 is not present after swap"); 657 REQUIRE_MESSAGE((AllowMultimapping<T>{} ? (cont.size() == 3) : (cont.size() == 2)), "Assignment operator has not copied the elements properly"); 658 659 T newcont_bkp = newcont; 660 newcont.swap(newcont); 661 REQUIRE_MESSAGE(newcont == newcont_bkp, "Unexpected swap-with-itself behavior"); 662 663 test_comparison_operators<T>(); 664 665 SpecialTests<T>::Test(); 666 } 667 668 template <typename Container> 669 class FillTable { 670 Container& my_table; 671 const int my_items; 672 bool my_asymptotic; 673 674 using pair_ib = std::pair<typename Container::iterator, bool>; 675 public: 676 FillTable(Container& table, int items, bool asymptotic) 677 : my_table(table), my_items(items), my_asymptotic(asymptotic) 678 { 679 REQUIRE((!(items&1) && items > 100)); 680 } 681 682 void operator()( int thread_index ) const { 683 if (thread_index == 0) { // Fill even keys forward (single thread) 684 bool last_inserted = true; 685 686 for (int i = 0; i < my_items; i += 2) { 687 int val = my_asymptotic ? 1 : i; 688 pair_ib pib = my_table.insert(Value<Container>::make(val)); 689 REQUIRE_MESSAGE((Value<Container>::get(*(pib.first)) == val), 690 "Element not properly inserted"); 691 REQUIRE_MESSAGE((last_inserted || !pib.second), 692 "Previous key was not inserted but current key is inserted"); 693 last_inserted = pib.second; 694 } 695 } else if (thread_index == 1) { // Fill even keys backward (single thread) 696 bool last_inserted = true; 697 698 for (int i = my_items - 2; i >= 0; i -= 2) { 699 int val = my_asymptotic ? 1 : i; 700 pair_ib pib = my_table.insert(Value<Container>::make(val)); 701 REQUIRE_MESSAGE((Value<Container>::get(*(pib.first)) == val), 702 "Element not properly inserted"); 703 REQUIRE_MESSAGE((last_inserted || !pib.second), 704 "Previous key was not inserted but current key is inserted"); 705 last_inserted = pib.second; 706 } 707 } else if (!(thread_index & 1)) { // Fill odd keys forward (multiple threads) 708 for (int i = 1; i < my_items; i += 2) { 709 if (i % 32 == 1 && i + 6 < my_items) { 710 if (my_asymptotic) { 711 auto init = { Value<Container>::make(1), Value<Container>::make(1), Value<Container>::make(1) }; 712 my_table.insert(init); 713 REQUIRE_MESSAGE(Value<Container>::get(*my_table.find(1)) == 1, "Element not properly inserted"); 714 } else { 715 auto init = { Value<Container>::make(i), Value<Container>::make(i + 2), 716 Value<Container>::make(i + 4) }; 717 my_table.insert(init); 718 REQUIRE_MESSAGE(Value<Container>::get(*my_table.find(i)) == i, "Element i not properly inserted"); 719 REQUIRE_MESSAGE(Value<Container>::get(*my_table.find(i + 2)) == i + 2, "Element i + 2 not properly inserted"); 720 REQUIRE_MESSAGE(Value<Container>::get(*my_table.find(i + 4)) == i + 4, "Element i + 4 not properly inserted"); 721 } 722 i += 4; 723 } else { 724 pair_ib pib = my_table.insert(Value<Container>::make(my_asymptotic ? 1 : i)); 725 REQUIRE_MESSAGE(Value<Container>::get(*(pib.first)) == (my_asymptotic ? 1 : i), "Element not properly inserted"); 726 } 727 } 728 } else { // Check odd keys backward (multiple threads) 729 if (!my_asymptotic) { 730 bool last_found = false; 731 for (int i = my_items - 1; i >= 0; i -= 2) { 732 typename Container::iterator it = my_table.find(i); 733 734 if (it != my_table.end()) { // found 735 REQUIRE_MESSAGE(Value<Container>::get(*it) == i, "Element not properly inserted"); 736 last_found = true; 737 } else { 738 REQUIRE_MESSAGE(!last_found, "Previous key was found, but current was not found"); 739 } 740 } 741 } 742 } 743 } 744 }; // class FillTable 745 746 template <typename Container, typename Range> 747 struct ParallelTraverseBody { 748 const int n; 749 atomic_byte_type* const array; 750 751 ParallelTraverseBody( atomic_byte_type arr[], int num ) 752 : n(num), array(arr) {} 753 754 void operator()( const Range& range ) const { 755 for (auto i = range.begin(); i != range.end(); ++i) { 756 int k = static_cast<int>(Value<Container>::key(*i)); 757 REQUIRE(k == Value<Container>::get(*i)); 758 REQUIRE(0 <= k); 759 REQUIRE(k < n); 760 ++array[k]; 761 } 762 } 763 }; // struct ParallelTraverseBody 764 765 template<typename T> 766 class CheckTable : utils::NoAssign { 767 T& table; 768 public: 769 CheckTable(T& t) : NoAssign(), table(t) {} 770 void operator()(int i) const { 771 int c = (int)table.count(i); 772 CHECK_MESSAGE(c, "must exist"); 773 } 774 }; 775 776 template <typename Container> 777 void test_concurrent_common( bool asymptotic = false ) { 778 #if __TBB_USE_ASSERT 779 int items = 2000; 780 #else 781 int items = 20000; 782 #endif 783 int items_inserted = 0; 784 int num_threads = 16; 785 786 Container table; 787 788 if (AllowMultimapping<Container>::value) { 789 // TODO: comment 790 items = 4 * items / (num_threads + 2); 791 items_inserted = items + (num_threads - 2) * items / 4; 792 } else { 793 items_inserted = items; 794 } 795 796 utils::NativeParallelFor(num_threads, FillTable<Container>(table, items, asymptotic)); 797 798 REQUIRE(int(table.size()) == items_inserted); 799 800 if (!asymptotic) { 801 atomic_byte_type* array = new atomic_byte_type[items]; 802 std::memset(static_cast<void*>(array), 0, items * sizeof(atomic_byte_type)); 803 804 typename Container::range_type r = table.range(); 805 806 auto p = CheckRecursiveRange<Container, typename Container::iterator>(r); 807 REQUIRE(items_inserted == p.first); 808 809 tbb::parallel_for(r, ParallelTraverseBody<Container, typename Container::range_type>(array, items)); 810 CheckRange(array, items, AllowMultimapping<Container>::value, (num_threads - 1)/2); 811 812 const Container& const_table = table; 813 std::memset(static_cast<void*>(array), 0, items * sizeof(atomic_byte_type)); 814 typename Container::const_range_type cr = const_table.range(); 815 816 p = CheckRecursiveRange<Container, typename Container::const_iterator>(cr); 817 REQUIRE(items_inserted == p.first); 818 819 tbb::parallel_for(cr, ParallelTraverseBody<Container, typename Container::const_range_type>(array, items)); 820 CheckRange(array, items, AllowMultimapping<Container>::value, (num_threads - 1) / 2); 821 delete[] array; 822 823 tbb::parallel_for(0, items, CheckTable<Container>(table)); 824 } 825 826 table.clear(); 827 // TODO: add check for container allocator counters 828 } 829 830 template <typename ContainerTraits> 831 void test_rvalue_ref_support() { 832 using namespace move_support_tests; 833 test_move_constructor<ContainerTraits>(); 834 test_move_assignment<ContainerTraits>(); 835 #if TBB_USE_EXCEPTIONS 836 test_ex_move_constructor<ContainerTraits>(); 837 #endif 838 } 839 840 template <typename Container> 841 void test_range_based_for_support() { 842 using namespace range_based_for_support_tests; 843 844 Container cont; 845 const int sequence_length = 100; 846 847 for (int i = 1; i <= sequence_length; ++i) { 848 cont.insert(Value<Container>::make(i)); 849 } 850 851 auto range_based_for_result = range_based_for_accumulate(cont, UnifiedSummer{}, 0); 852 auto reference_result = gauss_summ_of_int_sequence(sequence_length); 853 REQUIRE_MESSAGE(range_based_for_result == reference_result, 854 "Incorrect accumulated value generated via range based for"); 855 } 856 857 template <typename Container> 858 void test_initializer_list_support( std::initializer_list<typename Container::value_type> init ) { 859 using namespace initializer_list_support_tests; 860 861 test_initializer_list_support_without_assign<Container, TestInsertMethod>(init); 862 test_initializer_list_support_without_assign<Container, TestInsertMethod>({}); 863 } 864 865 template <typename Checker> 866 void test_set_specific_types() { 867 // TODO: add tests for atomics 868 Checker check_types; 869 const int num = 10; 870 871 // Test int 872 std::list<int> arr_int; 873 for (int i = 0; i != num; ++i) { 874 arr_int.emplace_back(i); 875 } 876 check_types.template check</*DefCtorPresent = */true>(arr_int); 877 878 // Test reference_wrapper 879 std::list<std::reference_wrapper<int>> arr_ref; 880 for (auto it = arr_int.begin(); it != arr_int.end(); ++it) { 881 arr_ref.emplace_back(*it); 882 } 883 check_types.template check</*DefCtorPresent = */false>(arr_ref); 884 885 // Test share_ptr 886 std::list<std::shared_ptr<int>> arr_shr; 887 for (int i = 0; i != num; ++i) { 888 arr_shr.emplace_back(std::make_shared<int>(i)); 889 } 890 check_types.template check</*DefCtorPresent = */true>(arr_shr); 891 892 // Test weak_ptr 893 std::list<std::weak_ptr<int>> arr_weak; 894 std::copy(arr_shr.begin(), arr_shr.end(), std::back_inserter(arr_weak)); 895 check_types.template check</*DefCtorPresent = */true>(arr_weak); 896 897 // Test std::pair 898 std::list<std::pair<int, int>> arr_pairs; 899 for (int i = 0; i != num; ++i) { 900 arr_pairs.emplace_back(i, i); 901 } 902 check_types.template check</*DefCtorPresent = */true>(arr_pairs); 903 904 // Test std::basic_string 905 std::list<std::basic_string<char, std::char_traits<char>, tbb::tbb_allocator<char>>> arr_strings; 906 for (int i = 0; i != num; ++i) { 907 arr_strings.emplace_back(i, char(i)); 908 } 909 check_types.template check</*DefCtorPresent = */true>(arr_strings); 910 } 911 912 template <typename Checker> 913 void test_map_specific_types() { 914 // TODO: add tests for int-atomic pairs 915 Checker check_types; 916 const int num = 10; 917 918 // Test int-int pairs 919 std::list<std::pair<const int, int>> arr_int_int_pairs; 920 for (int i = 0; i != num; ++i) { 921 arr_int_int_pairs.emplace_back(i, num - i); 922 } 923 check_types.template check</*DefCtorPresent = */true>(arr_int_int_pairs); 924 925 // Test reference_wrapper-int pairs 926 std::list<std::pair<const std::reference_wrapper<const int>, int>> arr_ref_int_pairs; 927 for (auto& item : arr_int_int_pairs) { 928 arr_ref_int_pairs.emplace_back(item.first, item.second); 929 } 930 check_types.template check</*DefCtorPresent = */true>(arr_ref_int_pairs); 931 932 // Test int-reference_wrapper pairs 933 std::list<std::pair<const int, std::reference_wrapper<int>>> arr_int_ref_pairs; 934 for (auto& item : arr_int_int_pairs) { 935 arr_int_ref_pairs.emplace_back(item.first, item.second); 936 } 937 check_types.template check</*DefCtorPresent = */false>(arr_int_ref_pairs); 938 939 // Test shared_ptr 940 std::list<std::pair<const std::shared_ptr<int>, std::shared_ptr<int>>> arr_shared_pairs; 941 for (int i = 0; i != num; ++i) { 942 const int num_minus_i = num - i; 943 arr_shared_pairs.emplace_back(std::make_shared<int>(i), std::make_shared<int>(num_minus_i)); 944 } 945 check_types.template check</*DefCtorPresent = */true>(arr_shared_pairs); 946 947 // Test weak_ptr 948 std::list<std::pair<const std::weak_ptr<int>, std::weak_ptr<int>>> arr_wick_pairs; 949 std::copy(arr_shared_pairs.begin(), arr_shared_pairs.end(), std::back_inserter(arr_wick_pairs)); 950 check_types.template check</*DefCtorPresent = */true>(arr_wick_pairs); 951 952 // Test std::pair 953 using pair_key_type = std::pair<int, int>; 954 std::list<std::pair<const pair_key_type, int>> arr_pair_int_pairs; 955 for (int i = 0; i != num; ++i) { 956 arr_pair_int_pairs.emplace_back(pair_key_type{i, i}, i); 957 } 958 check_types.template check</*DefCtorPresent = */true>(arr_pair_int_pairs); 959 960 // Test std::basic_string 961 using tbb_string_key_type = std::basic_string<char, std::char_traits<char>, tbb::tbb_allocator<char>>; 962 std::list<std::pair<const tbb_string_key_type, int>> arr_tbb_string_pairs; 963 for (int i = 0; i != num; ++i) { 964 tbb_string_key_type key(i, char(i)); 965 arr_tbb_string_pairs.emplace_back(key, i); 966 } 967 check_types.template check</*DefCtorPresent = */true>(arr_tbb_string_pairs); 968 } 969 970 namespace test { 971 972 // For the sake of simplified testing, make std::unique_ptr implicitly convertible to/from the pointer 973 template <typename T> 974 class unique_ptr : public std::unique_ptr<T> { 975 public: 976 using pointer = typename std::unique_ptr<T>::pointer; 977 978 unique_ptr( pointer p ) : std::unique_ptr<T>(p) {} 979 operator pointer() const { return this->get(); } 980 }; // class unique_ptr 981 982 } // namespace test 983 984 namespace std { 985 template <typename T> 986 struct hash<test::unique_ptr<T>> { 987 std::size_t operator()(const test::unique_ptr<T>& ptr) const { 988 return std::hash<std::unique_ptr<T>>{}(ptr); 989 } 990 }; 991 } 992 993 template <bool Condition> 994 struct CallIf { 995 template <typename Func> 996 void operator()( Func func ) const { func(); } 997 }; 998 999 template <> 1000 struct CallIf<false> { 1001 template <typename Func> 1002 void operator()( Func ) const {} 1003 }; 1004 1005 template <typename Table> 1006 class TestOperatorSquareBrackets { 1007 using value_type = typename Table::value_type; 1008 Table& my_c; 1009 const value_type& my_value; 1010 public: 1011 TestOperatorSquareBrackets( Table& c, const value_type& value ) 1012 : my_c(c), my_value(value) {} 1013 1014 void operator()() const { 1015 utils::IsEqual equal; 1016 REQUIRE(equal(my_c[my_value.first], my_value.second)); 1017 typename Table::key_type temp_key = my_value.first; 1018 REQUIRE(equal(my_c[std::move(temp_key)], my_value.second)); 1019 } 1020 }; // class TestOperatorSquareBrackets 1021 1022 template <bool DefCtorPresent, typename Table, typename Value> 1023 void TestSquareBracketsAndAt( Table&, const Value&, /*multimap = */std::true_type ) { 1024 // operator [] and at are not presented in the multimap 1025 } 1026 1027 template <bool DefCtorPresent, typename Table, typename Value> 1028 void TestSquareBracketsAndAt( Table& c, const Value& value, /*multimap = */std::false_type ) { 1029 CallIf<DefCtorPresent>()(TestOperatorSquareBrackets<Table>(c, value)); 1030 utils::IsEqual equal; 1031 REQUIRE(equal(c.at(value.first), value.second)); 1032 // TODO: add test for at exceptions 1033 const Table& constC = c; 1034 REQUIRE(equal(constC.at(value.first), value.second)); 1035 } 1036 1037 template <bool DefCtorPresent, typename Table, typename Value> 1038 void TestMapSpecificMethods( Table&, const Value& ) {} 1039 1040 template <bool DefCtorPresent, typename Table> 1041 void TestMapSpecificMethods( Table& c, const std::pair<const typename Table::key_type, 1042 typename Table::mapped_type>& value ) 1043 { 1044 TestSquareBracketsAndAt<DefCtorPresent>(c, value, std::integral_constant<bool, AllowMultimapping<Table>::value>{}); 1045 } 1046 1047 template <bool DefCtorPresent, typename Table> 1048 class CheckValue { 1049 Table& my_c; 1050 public: 1051 CheckValue( Table& c ) : my_c(c) {} 1052 void operator()( const typename Table::value_type& value ) { 1053 using iterator = typename Table::iterator; 1054 using const_iterator = typename Table::const_iterator; 1055 const Table& constC = my_c; 1056 // count 1057 REQUIRE(my_c.count(Value<Table>::key(value)) == 1); 1058 // find 1059 utils::IsEqual equal; 1060 REQUIRE(equal(*my_c.find(Value<Table>::key(value)), value)); 1061 REQUIRE(equal(*constC.find(Value<Table>::key(value)), value)); 1062 // erase 1063 REQUIRE(my_c.unsafe_erase(Value<Table>::key(value)) != 0); 1064 REQUIRE(my_c.unsafe_erase(Value<Table>::key(value)) == 0); 1065 // insert 1066 std::pair<iterator, bool> res = my_c.insert(value); 1067 REQUIRE(equal(*res.first, value)); 1068 REQUIRE(res.second); 1069 // erase 1070 iterator it = res.first; 1071 ++it; 1072 REQUIRE(my_c.unsafe_erase(res.first) == it); 1073 // insert 1074 REQUIRE(equal(*my_c.insert(my_c.begin(), value), value)); 1075 // equal_range 1076 std::pair<iterator, iterator> r1 = my_c.equal_range(Value<Table>::key(value)); 1077 REQUIRE((equal(*r1.first, value) && ++r1.first == r1.second)); 1078 std::pair<const_iterator, const_iterator> r2 = constC.equal_range(Value<Table>::key(value)); 1079 REQUIRE((equal(*r2.first, value) && ++r2.first == r2.second)); 1080 1081 TestMapSpecificMethods<DefCtorPresent>(my_c, value); 1082 } 1083 }; // class CheckValue 1084 1085 namespace detail { 1086 1087 #if (__INTEL_COMPILER || __clang__ ) && __TBB_GLIBCXX_VERSION && __TBB_GLIBCXX_VERSION < 40900 1088 template <typename T> 1089 struct assignable_atomic : std::atomic<T> { 1090 using std::atomic<T>::operator=; 1091 assignable_atomic& operator=(const assignable_atomic& a) { 1092 store(a.load(std::memory_order_relaxed), std::memory_order_relaxed); 1093 } 1094 }; 1095 1096 template <typename T> 1097 using atomic_type = assignable_atomic<T>; 1098 #else 1099 template <typename T> 1100 using atomic_type = std::atomic<T>; 1101 #endif 1102 } 1103 1104 template <typename Value> 1105 class TestRange { 1106 const std::list<Value>& my_lst; 1107 std::vector<detail::atomic_type<bool>>& my_marks; 1108 public: 1109 TestRange( const std::list<Value>& lst, std::vector<detail::atomic_type<bool>>& marks ) 1110 : my_lst(lst), my_marks(marks) 1111 { 1112 std::fill(my_marks.begin(), my_marks.end(), false); 1113 } 1114 1115 template <typename Range> 1116 void operator()( const Range& r ) const { 1117 do_test_range(r.begin(), r.end()); 1118 } 1119 1120 template <typename Iterator> 1121 void do_test_range( Iterator i, Iterator j ) const { 1122 for (Iterator it = i; it != j;) { 1123 Iterator prev_it = it++; 1124 auto it2 = std::search(my_lst.begin(), my_lst.end(), prev_it, it, utils::IsEqual()); 1125 REQUIRE(it2 != my_lst.end()); 1126 auto dist = std::distance(my_lst.begin(), it2); 1127 REQUIRE(!my_marks[dist]); 1128 my_marks[dist] = true; 1129 } 1130 } 1131 }; // class TestRange 1132 1133 template <bool DefCtorPresent, typename Table> 1134 void CommonExamine( Table c, const std::list<typename Table::value_type>& lst ) { 1135 using value_type = typename Table::value_type; 1136 1137 if (!(!c.empty() && c.size() == lst.size() && c.max_size() >= c.size())) { 1138 std::cout << std::boolalpha; 1139 std::cout << "Empty? " << c.empty() << std::endl; 1140 std::cout << "sizes equal? " << (c.size() == lst.size()) << std::endl; 1141 std::cout << "\t" << c.size() << std::endl; 1142 std::cout << "\t" << lst.size() << std::endl; 1143 std::cout << "Max size greater? " << (c.max_size() >= c.size()) << std::endl; 1144 } 1145 REQUIRE((!c.empty() && c.size() == lst.size() && c.max_size() >= c.size())); 1146 1147 std::for_each(lst.begin(), lst.end(), CheckValue<DefCtorPresent, Table>(c)); 1148 1149 std::vector<detail::atomic_type<bool>> marks(lst.size()); 1150 1151 TestRange<value_type>(lst, marks).do_test_range(c.begin(), c.end()); 1152 REQUIRE(std::find(marks.begin(), marks.end(), false) == marks.end()); 1153 1154 const Table constC = c; 1155 REQUIRE(c.size() == constC.size()); 1156 1157 TestRange<value_type>(lst, marks).do_test_range(c.begin(), c.end()); 1158 REQUIRE(std::find(marks.begin(), marks.end(), false) == marks.end()); 1159 1160 tbb::parallel_for(c.range(), TestRange<value_type>(lst, marks)); 1161 REQUIRE(std::find(marks.begin(), marks.end(), false) == marks.end()); 1162 1163 tbb::parallel_for(constC.range(), TestRange<value_type>(lst, marks)); 1164 REQUIRE(std::find(marks.begin(), marks.end(), false) == marks.end()); 1165 1166 Table c2; 1167 auto begin5 = lst.begin(); 1168 std::advance(begin5, 5); 1169 c2.insert(lst.begin(), begin5); 1170 std::for_each(lst.begin(), begin5, CheckValue<DefCtorPresent, Table>(c2)); 1171 1172 c2.swap(c); 1173 REQUIRE(c2.size() == lst.size()); 1174 REQUIRE(c.size() == 5); 1175 1176 std::for_each(lst.begin(), lst.end(), CheckValue<DefCtorPresent, Table>(c2)); 1177 1178 c2.clear(); 1179 REQUIRE(c2.size() == 0); 1180 1181 auto alloc = c.get_allocator(); 1182 value_type* ptr = alloc.allocate(1); 1183 REQUIRE(ptr != nullptr); 1184 alloc.deallocate(ptr, 1); 1185 } 1186 1187 template <typename ContainerTraits> 1188 void test_scoped_allocator() { 1189 using allocator_data_type = AllocatorAwareData<std::scoped_allocator_adaptor<std::allocator<int>>>; 1190 using basic_allocator_type = std::scoped_allocator_adaptor<std::allocator<allocator_data_type>>; 1191 using container_value_type = typename ContainerTraits::template container_value_type<allocator_data_type>; 1192 using allocator_type = typename std::allocator_traits<basic_allocator_type>::template rebind_alloc<container_value_type>; 1193 using container_type = typename ContainerTraits::template container_type<allocator_data_type, allocator_type>; 1194 1195 allocator_type allocator; 1196 allocator_data_type key1(1, allocator); 1197 allocator_data_type key2(2, allocator); 1198 1199 container_value_type value1 = Value<container_type>::make(key1); 1200 container_value_type value2 = Value<container_type>::make(key2); 1201 1202 auto init_list = { value1, value2 }; 1203 1204 container_type c1(allocator); 1205 container_type c2(allocator); 1206 1207 allocator_data_type::activate(); 1208 1209 emplace_helpers::call_emplace(c1, key1); 1210 emplace_helpers::call_emplace(c2, std::move(key2)); 1211 1212 c1.clear(); 1213 c2.clear(); 1214 1215 c1.insert(value1); 1216 c2.insert(std::move(value2)); 1217 1218 c1.clear(); 1219 c2.clear(); 1220 1221 c1.insert(init_list); 1222 c2.insert(value1); 1223 1224 c1 = c2; 1225 c2 = std::move(c1); 1226 1227 allocator_data_type::deactivate(); 1228 } 1229 1230 1231 struct int_key { 1232 int my_item; 1233 int_key(int i) : my_item(i) {} 1234 }; 1235 1236 bool operator==(const int_key& ik, int i) { return ik.my_item == i; } 1237 bool operator==(int i, const int_key& ik) { return i == ik.my_item; } 1238 bool operator==(const int_key& ik1, const int_key& ik2) { return ik1.my_item == ik2.my_item; } 1239 1240 bool operator<( const int_key& ik, int i ) { return ik.my_item < i; } 1241 bool operator<( int i, const int_key& ik ) { return i < ik.my_item; } 1242 bool operator<( const int_key& ik1, const int_key& ik2 ) { return ik1.my_item < ik2.my_item; } 1243 1244 struct char_key { 1245 const char* my_item; 1246 char_key(const char* c) : my_item(c) {} 1247 1248 const char& operator[] (std::size_t pos) const { 1249 return my_item[pos]; 1250 } 1251 1252 std::size_t size() const { 1253 return std::strlen(my_item); 1254 } 1255 }; 1256 1257 bool operator==(const char_key& ck, std::string c) { 1258 std::size_t i = 0; 1259 while (ck[i] != '\0' && i < c.size() && ck[i] == c[i]) { ++i;} 1260 return c.size() == i && ck[i] == '\0'; 1261 } 1262 bool operator==(std::string c, const char_key& ck) { 1263 return ck == c; 1264 } 1265 bool operator==(const char_key& ck1, const char_key& ck2) { 1266 std::size_t i = 0; 1267 while (ck1[i] != '\0' && ck2[i] != '\0' && ck1[i] == ck2[i]) { ++i;} 1268 return ck1[i] == ck2[i]; 1269 } 1270 1271 bool operator<( const char_key& ck, std::string c ) { 1272 return std::lexicographical_compare(ck.my_item, ck.my_item + ck.size(), c.begin(), c.end()); 1273 } 1274 1275 bool operator<(std::string c, const char_key& ck) { 1276 return std::lexicographical_compare(c.begin(), c.end(), ck.my_item, ck.my_item + ck.size()); 1277 } 1278 1279 bool operator<( const char_key& ck1, const char_key& ck2 ) { 1280 return std::lexicographical_compare(ck1.my_item, ck1.my_item + ck1.size(), ck2.my_item, ck2.my_item + ck2.size()); 1281 } 1282 1283 struct equal_to { 1284 using is_transparent = int; 1285 template <typename T, typename W> 1286 bool operator()(const T &lhs, const W &rhs) const { 1287 return lhs == rhs; 1288 } 1289 }; 1290 1291 struct hash_with_transparent_key_equal { 1292 template <typename T> 1293 size_t operator()(const T& key) const { return hash(key); } 1294 using transparent_key_equal = equal_to; 1295 int prime = 433494437; 1296 int first_factor = 41241245; 1297 int second_factor = 2523422; 1298 1299 size_t hash(const int& key) const { return (first_factor * key + second_factor) % prime; } 1300 1301 size_t hash(const int_key& key) const { return (first_factor * key.my_item + second_factor) % prime; } 1302 1303 size_t hash(const std::string& key) const { 1304 int sum = 0; 1305 for (auto it = key.begin(); it != key.end(); ++it) { 1306 sum += first_factor * (*it) + second_factor; 1307 } 1308 return sum % prime; 1309 } 1310 1311 size_t hash(const char_key& key) const { 1312 int sum = 0; 1313 int i = 0; 1314 while (key[i] != '\0') { 1315 sum += first_factor * key[i++] + second_factor; 1316 } 1317 return sum % prime; 1318 } 1319 1320 }; 1321 1322 template <typename Container> 1323 void check_heterogeneous_functions_key_int_impl() { 1324 static_assert(std::is_same<typename Container::key_type, int>::value, 1325 "incorrect key_type for heterogeneous lookup test"); 1326 // Initialization 1327 Container c; 1328 int size = 10; 1329 for (int i = 0; i < size; i++) { 1330 c.insert(Value<Container>::make(i)); 1331 } 1332 // Insert first duplicated element for multicontainers 1333 if (AllowMultimapping<Container>::value) { 1334 c.insert(Value<Container>::make(0)); 1335 } 1336 1337 // Look up testing 1338 for (int i = 0; i < size; i++) { 1339 int_key k(i); 1340 int key = i; 1341 REQUIRE_MESSAGE(c.find(k) == c.find(key), "Incorrect heterogeneous find return value"); 1342 REQUIRE_MESSAGE(c.count(k) == c.count(key), "Incorrect heterogeneous count return value"); 1343 } 1344 1345 // unsafe_extract testing 1346 for (int i = 0; i < size; i++) { 1347 Container extract_c = c; 1348 int_key int_k(i); 1349 auto int_key_extract = extract_c.unsafe_extract(int_k); 1350 if (!AllowMultimapping<Container>::value) { 1351 REQUIRE_MESSAGE(extract_c.find(int_k) == extract_c.end(), "Key exists after extract"); 1352 } 1353 REQUIRE_MESSAGE(!int_key_extract.empty(), "Empty node with exists key"); 1354 REQUIRE_MESSAGE(node_handling_tests::compare_handle_getters(int_key_extract, Value<Container>::make(i)), "Incorrect node"); 1355 } 1356 1357 // unsafe_extract not exists key 1358 auto not_exists = c.unsafe_extract(int_key(100)); 1359 REQUIRE_MESSAGE(not_exists.empty(), "Not empty node with not exists key"); 1360 1361 // multimap unsafe_extract testing 1362 if (AllowMultimapping<Container>::value) { 1363 Container extract_m; 1364 for (int i = 0; i < size; i++) { 1365 extract_m.insert(Value<Container>::make(i)); 1366 extract_m.insert(Value<Container>::make(i, i + 1)); 1367 } 1368 for (int i = 0; i < size; i++) { 1369 int_key int_k(i); 1370 auto int_key_extract = extract_m.unsafe_extract(int_k); 1371 REQUIRE_MESSAGE(!int_key_extract.empty(), "Empty node with exists key"); 1372 REQUIRE_MESSAGE((node_handling_tests::compare_handle_getters(int_key_extract, Value<Container>::make(i, i)) || 1373 node_handling_tests::compare_handle_getters(int_key_extract, Value<Container>::make(i, i + 1))), "Incorrect node"); 1374 REQUIRE_MESSAGE(extract_m.find(int_k) != extract_m.end(), "All nodes for key deleted"); 1375 } 1376 } 1377 1378 // Erase testing 1379 for (int i = 0; i < size; i++) { 1380 auto count_before_erase = c.count(i); 1381 auto result = c.unsafe_erase(int_key(i)); 1382 REQUIRE_MESSAGE(count_before_erase == result,"Incorrect erased elements count"); 1383 REQUIRE_MESSAGE(c.count(i) == 0, "Some elements was not erased"); 1384 } 1385 1386 } 1387 1388 template <typename Container> 1389 void check_heterogeneous_functions_key_string_impl() { 1390 static_assert(std::is_same<typename Container::key_type, std::string>::value, 1391 "incorrect key_type for heterogeneous lookup test"); 1392 // Initialization 1393 std::vector<const char*> keys{"key1", "key2", "key3", "key4", 1394 "key5", "key6", "key7", "key8", "key9", "key10"}; 1395 std::vector<const char*> values{"value1", "value2", "value3", "value4", 1396 "value5", "value6", "value7", "value8", "value9", "value10", "value11"}; 1397 Container c; 1398 for (auto it = keys.begin(); it != keys.end(); ++it) { 1399 c.insert(Value<Container>::make(*it)); 1400 } 1401 // Insert first duplicated element for multicontainers 1402 if (AllowMultimapping<Container>::value) { 1403 c.insert(Value<Container>::make(*keys.begin())); 1404 } 1405 1406 // Look up testing 1407 for (auto it = keys.begin(); it != keys.end(); ++it) { 1408 std::string key = *it; 1409 char_key k{*it}; 1410 REQUIRE_MESSAGE(c.find(k) == c.find(key), "Incorrect heterogeneous find return value"); 1411 REQUIRE_MESSAGE(c.count(k) == c.count(key), "Incorrect heterogeneous count return value"); 1412 } 1413 1414 // unsafe_extract testing 1415 for (auto it = keys.begin(); it != keys.end(); ++it) { 1416 Container extract_c = c; 1417 char_key k{*it}; 1418 auto char_key_extract = extract_c.unsafe_extract(k); 1419 REQUIRE_MESSAGE(!char_key_extract.empty(), "Empty node with exists key"); 1420 REQUIRE_MESSAGE(node_handling_tests::compare_handle_getters(char_key_extract, Value<Container>::make(*it)), "Incorrect node"); 1421 } 1422 1423 // unsafe_extract not exists key 1424 auto not_exists = c.unsafe_extract(char_key("not exists")); 1425 REQUIRE_MESSAGE(not_exists.empty(), "Not empty node with not exists key"); 1426 1427 // multimap unsafe_extract testing 1428 if (AllowMultimapping<Container>::value){ 1429 Container extract_m; 1430 for (std::size_t i = 0; i < keys.size(); i++) { 1431 extract_m.insert(Value<Container>::make(keys[i], values[i])); 1432 extract_m.insert(Value<Container>::make(keys[i], values[i + 1])); 1433 } 1434 for (std::size_t i = 0; i < keys.size(); i++) { 1435 char_key char_k(keys[i]); 1436 auto char_key_extract = extract_m.unsafe_extract(char_k); 1437 REQUIRE_MESSAGE(!char_key_extract.empty(), "Empty node with exists key"); 1438 REQUIRE_MESSAGE((node_handling_tests::compare_handle_getters(char_key_extract, Value<Container>::make(keys[i], values[i])) || 1439 node_handling_tests::compare_handle_getters(char_key_extract, Value<Container>::make(keys[i], values[i + 1]))), "Incorrect node"); 1440 REQUIRE_MESSAGE(extract_m.find(char_k) != extract_m.end(), "All nodes for key deleted"); 1441 } 1442 } 1443 1444 // Erase testing 1445 for (auto it = keys.begin(); it != keys.end(); ++it) { 1446 std::string key = *it; 1447 char_key k{*it}; 1448 auto count_before_erase = c.count(key); 1449 auto result = c.unsafe_erase(k); 1450 REQUIRE_MESSAGE(count_before_erase==result,"Incorrect erased elements count"); 1451 REQUIRE_MESSAGE(c.count(key)==0, "Some elements was not erased"); 1452 } 1453 } 1454 1455 struct CountingKey { 1456 static std::size_t counter; 1457 1458 CountingKey() { ++counter; } 1459 CountingKey( const CountingKey& ) { ++counter; } 1460 ~CountingKey() {} 1461 CountingKey& operator=( const CountingKey& ) { return *this; } 1462 1463 static void reset() { counter = 0; } 1464 }; 1465 1466 std::size_t CountingKey::counter; 1467 1468 namespace std { 1469 template <> 1470 struct hash<CountingKey> { 1471 std::size_t operator()( const CountingKey& ) const { return 0; } 1472 }; 1473 } 1474 1475 bool operator==( const CountingKey&, const CountingKey& ) { return true; } 1476 1477 bool operator<( const CountingKey&, const CountingKey& ) { return true; } 1478 1479 struct int_constructible_object { 1480 int_constructible_object(int k) : key(k) {} 1481 int key; 1482 }; // struct int_constructible_object 1483 1484 bool operator==( const int_constructible_object& lhs, const int_constructible_object rhs ) { 1485 return lhs.key == rhs.key; 1486 } 1487 1488 // A test for 1489 // template <typename P> insert(P&&) in maps 1490 template <template <typename...> class Container> 1491 void test_insert_by_generic_pair() { 1492 using value_type = std::pair<const int, int_constructible_object>; 1493 using generic_pair_type = std::pair<int, int>; 1494 1495 static_assert(std::is_constructible<value_type, generic_pair_type>::value, 1496 "Incorrect test setup"); 1497 1498 Container<int, int_constructible_object> cont1, cont2; 1499 using iterator = typename Container<int, int_constructible_object>::iterator; 1500 1501 for (int i = 0; i != 10; ++i) { 1502 std::pair<iterator, bool> res_generic_insert = cont1.insert(generic_pair_type(1, i)); 1503 std::pair<iterator, bool> res_value_insert = cont2.insert(value_type(1, int_constructible_object(i))); 1504 1505 REQUIRE_MESSAGE(*res_generic_insert.first == *res_value_insert.first, "Insert by generic pair returned wrong iterator"); 1506 REQUIRE_MESSAGE(res_generic_insert.second == res_value_insert.second, "Insert by generic pair returned wrong insertion value"); 1507 } 1508 1509 for (int i = 0; i != 10; ++i) { 1510 iterator res_generic_insert_hint = cont1.insert(cont1.cbegin(), generic_pair_type(2, i)); 1511 iterator res_value_insert_hint = cont2.insert(cont2.cbegin(), value_type(2, int_constructible_object(i))); 1512 1513 REQUIRE_MESSAGE(*res_generic_insert_hint == *res_value_insert_hint, "Hinted insert by generic pair returned wrong iterator"); 1514 } 1515 1516 Container<CountingKey, int_constructible_object> counting_cont; 1517 using counting_generic_pair = std::pair<CountingKey, int>; 1518 1519 static_assert(std::is_constructible<typename decltype(counting_cont)::value_type, counting_generic_pair>::value, 1520 "Incorrect test setup"); 1521 1522 counting_generic_pair counting_pair(CountingKey{}, 1); 1523 CountingKey::reset(); 1524 1525 counting_cont.insert(counting_pair); 1526 REQUIRE_MESSAGE(CountingKey::counter == 1, "Only one element should be constructed in-place during the generic pair insertion"); 1527 1528 CountingKey::reset(); 1529 } 1530 1531 template <typename Container> 1532 void test_swap_not_always_equal_allocator() { 1533 static_assert(std::is_same<typename Container::allocator_type, NotAlwaysEqualAllocator<typename Container::value_type>>::value, 1534 "Incorrect allocator in not always equal test"); 1535 Container c1{}; 1536 Container c2{Value<Container>::make(1), Value<Container>::make(2)}; 1537 1538 Container c1_copy = c1; 1539 Container c2_copy = c2; 1540 1541 c1.swap(c2); 1542 1543 REQUIRE_MESSAGE(c1 == c2_copy, "Incorrect swap with not always equal allocator"); 1544 REQUIRE_MESSAGE(c2 == c1_copy, "Incorrect swap with not always equal allocator"); 1545 } 1546 1547 #if TBB_USE_EXCEPTIONS 1548 template <typename Container> 1549 void test_exception_on_copy_ctor() { 1550 Container c1; 1551 c1.emplace(Value<Container>::make(ThrowOnCopy{})); 1552 1553 using container_allocator_type = std::allocator<Container>; 1554 using alloc_traits = std::allocator_traits<container_allocator_type>; 1555 container_allocator_type container_allocator; 1556 Container* c2_ptr = alloc_traits::allocate(container_allocator, 1); 1557 1558 ThrowOnCopy::activate(); 1559 // Test copy ctor 1560 try { 1561 alloc_traits::construct(container_allocator, c2_ptr, c1); 1562 } catch ( int error_code ) { 1563 REQUIRE_MESSAGE(error_code == ThrowOnCopy::error_code(), "Incorrect code was thrown"); 1564 } 1565 1566 REQUIRE_MESSAGE(c2_ptr->empty(), "Incorrect container state after throwing copy constructor"); 1567 1568 alloc_traits::deallocate(container_allocator, c2_ptr, 1); 1569 c2_ptr = alloc_traits::allocate(container_allocator, 1); 1570 1571 // Test copy ctor with allocator 1572 try { 1573 auto value_allocator = c1.get_allocator(); 1574 alloc_traits::construct(container_allocator, c2_ptr, c1, value_allocator); 1575 } catch( int error_code ) { 1576 REQUIRE_MESSAGE(error_code == ThrowOnCopy::error_code(), "Incorrect code was thrown"); 1577 } 1578 1579 REQUIRE_MESSAGE(c2_ptr->empty(), "Incorrect container state after throwing copy ctor with allocator"); 1580 alloc_traits::deallocate(container_allocator, c2_ptr, 1); 1581 ThrowOnCopy::deactivate(); 1582 } 1583 #endif // TBB_USE_EXCEPTIONS 1584 1585 #endif // __TBB_test_common_concurrent_associative_common_H 1586