1 static const char help[] = "Tests UnorderedMap.\n"; 2 3 #include <petsc/private/cpp/unordered_map.hpp> 4 #include <petscviewer.h> 5 6 #include <sstream> // std::ostringstream 7 #include <string> 8 #include <vector> 9 #include <algorithm> // std::sort 10 11 // ========================================================================================== 12 // Setup 13 // ========================================================================================== 14 15 // see https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x 16 static inline void hash_combine(std::size_t &) noexcept { } 17 18 template <typename T, typename... Rest> 19 static inline void hash_combine(std::size_t &seed, const T &v, Rest &&...rest) noexcept 20 { 21 std::hash<T> hasher; 22 seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 23 hash_combine(seed, std::forward<Rest>(rest)...); 24 } 25 26 #define MAKE_HASHABLE(type, ...) \ 27 namespace std \ 28 { \ 29 template <> \ 30 struct hash<type> { \ 31 std::size_t operator()(const type &t) const noexcept \ 32 { \ 33 std::size_t ret = 0; \ 34 hash_combine(ret, __VA_ARGS__); \ 35 return ret; \ 36 } \ 37 }; \ 38 } 39 40 using pair_type = std::pair<int, double>; 41 MAKE_HASHABLE(pair_type, t.first, t.second); 42 43 using namespace Petsc::util; 44 45 struct Foo { 46 int x{}; 47 double y{}; 48 49 constexpr Foo() noexcept = default; 50 constexpr Foo(int x, double y) noexcept : x(x), y(y) { } 51 52 bool operator==(const Foo &other) const noexcept { return x == other.x && y == other.y; } 53 bool operator!=(const Foo &other) const noexcept { return !(*this == other); } 54 bool operator<(const Foo &other) const noexcept { return std::tie(x, y) < std::tie(other.x, other.y); } 55 56 PetscErrorCode to_string(std::string &buf) const noexcept 57 { 58 PetscFunctionBegin; 59 PetscCallCXX(buf = std::to_string(x) + ", " + std::to_string(y)); 60 PetscFunctionReturn(PETSC_SUCCESS); 61 } 62 63 friend std::ostream &operator<<(std::ostream &oss, const Foo &f) noexcept 64 { 65 std::string ret; 66 67 PetscFunctionBegin; 68 PetscCallAbort(PETSC_COMM_SELF, f.to_string(ret)); 69 oss << ret; 70 PetscFunctionReturn(oss); 71 } 72 }; 73 74 MAKE_HASHABLE(Foo, t.x, t.y); 75 76 struct Bar { 77 std::vector<int> x{}; 78 std::string y{}; 79 80 Bar() noexcept = default; 81 Bar(std::vector<int> x, std::string y) noexcept : x(std::move(x)), y(std::move(y)) { } 82 83 bool operator==(const Bar &other) const noexcept { return x == other.x && y == other.y; } 84 bool operator<(const Bar &other) const noexcept { return std::tie(x, y) < std::tie(other.x, other.y); } 85 86 PetscErrorCode to_string(std::string &buf) const noexcept 87 { 88 PetscFunctionBegin; 89 PetscCallCXX(buf = '<'); 90 for (std::size_t i = 0; i < x.size(); ++i) { 91 PetscCallCXX(buf += std::to_string(x[i])); 92 if (i + 1 != x.size()) PetscCallCXX(buf += ", "); 93 } 94 PetscCallCXX(buf += ">, <" + y + '>'); 95 PetscFunctionReturn(PETSC_SUCCESS); 96 } 97 98 friend std::ostream &operator<<(std::ostream &oss, const Bar &b) noexcept 99 { 100 std::string ret; 101 102 PetscFunctionBegin; 103 PetscCallAbort(PETSC_COMM_SELF, b.to_string(ret)); 104 oss << ret; 105 PetscFunctionReturn(oss); 106 } 107 }; 108 109 struct BadHash { 110 template <typename T> 111 constexpr std::size_t operator()(const T &) const noexcept 112 { 113 return 1; 114 } 115 }; 116 117 template <typename T> 118 struct Printer { 119 using signature = PetscErrorCode(const T &, std::string &); 120 121 mutable std::string buffer; 122 std::function<signature> printer; 123 124 template <typename F> 125 Printer(F &&printer) noexcept : printer(std::forward<F>(printer)) 126 { 127 } 128 129 PETSC_NODISCARD const char *operator()(const T &value) const noexcept 130 { 131 PetscFunctionBegin; 132 PetscCallAbort(PETSC_COMM_SELF, this->printer(value, this->buffer)); 133 PetscFunctionReturn(this->buffer.c_str()); 134 } 135 }; 136 137 #if defined(__GNUC__) 138 // gcc 6.4 through 7.5 have a visibility bug: 139 // 140 // error: 'MapTester<T>::test_insert()::<lambda(MapTester<T>::value_type&)> [with T = 141 // ...]::<lambda(...)>' declared with greater visibility than the type of its field 142 // 'MapTester<T>::test_insert()::<lambda(MapTester<T>::value_type&)> [with T = 143 // ...]::<lambda(const char*, const insert_return_type&) 144 // 145 // Error message implies that the visibility of the lambda in question is greater than the 146 // visibility of the capture list value "this". 147 // 148 // Since lambdas are translated into the classes with the operator()(...) and (it seems like) 149 // captured values are translated into the fields of this class it looks like for some reason 150 // the visibility of that class is higher than the one of those fields. 151 // 152 // see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80947 153 #if ((__GNUC__ == 6) && (__GNUC_MINOR__ >= 4)) || ((__GNUC__ == 7) && (__GNUC_MINOR__ <= 5)) 154 #define PETSC_GCC_LAMBDA_VISIBILITY_WORKAROUND 1 155 #endif 156 #endif 157 158 #ifdef PETSC_GCC_LAMBDA_VISIBILITY_WORKAROUND 159 #pragma GCC visibility push(hidden) 160 #endif 161 template <typename... T> 162 class MapTester { 163 public: 164 using map_type = Petsc::UnorderedMap<T...>; 165 using key_type = typename map_type::key_type; 166 using value_type = typename map_type::value_type; 167 using mapped_type = typename map_type::mapped_type; 168 169 const PetscViewer vwr; 170 const std::string map_name; 171 Printer<key_type> key_printer; 172 Printer<mapped_type> value_printer; 173 std::function<value_type(void)> generator; 174 175 PetscErrorCode view_map(const map_type &map) const noexcept 176 { 177 std::ostringstream oss; 178 179 PetscFunctionBegin; 180 PetscCallCXX(oss << std::boolalpha); 181 PetscCallCXX(oss << "map: '" << this->map_name << "'\n"); 182 PetscCallCXX(oss << " size: " << map.size() << '\n'); 183 PetscCallCXX(oss << " capacity: " << map.capacity() << '\n'); 184 PetscCallCXX(oss << " bucket count: " << map.bucket_count() << '\n'); 185 PetscCallCXX(oss << " empty: " << map.empty() << '\n'); 186 PetscCallCXX(oss << " flag bucket width: " << map_type::flag_bucket_width::value << '\n'); 187 PetscCallCXX(oss << " flag pairs per bucket: " << map_type::flag_pairs_per_bucket::value << '\n'); 188 PetscCallCXX(oss << " {\n"); 189 for (auto &&entry : map) PetscCallCXX(oss << " key: [" << this->key_printer(entry.first) << "] -> [" << this->value_printer(entry.second) << "]\n"); 190 PetscCallCXX(oss << " }\n"); 191 PetscCall(PetscViewerASCIIPrintf(vwr, "%s", oss.str().c_str())); 192 PetscFunctionReturn(PETSC_SUCCESS); 193 } 194 195 #define MapCheck(map__, cond__, comm__, ierr__, base_mess__, ...) \ 196 do { \ 197 if (PetscUnlikely(!(cond__))) { \ 198 PetscCall(this->view_map(map__)); \ 199 SETERRQ(comm__, ierr__, "%s: " base_mess__, this->map_name.c_str(), __VA_ARGS__); \ 200 } \ 201 } while (0) 202 203 PetscErrorCode check_size_capacity_coherent(map_type &map) const noexcept 204 { 205 const auto msize = map.size(); 206 const auto mcap = map.capacity(); 207 208 PetscFunctionBegin; 209 MapCheck(map, msize == map.size(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map size appears to change each time it is called! first call: %zu, second call %zu", msize, map.size()); 210 MapCheck(map, mcap == map.capacity(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity appears to change each time it is called! first call: %zu, second call %zu", mcap, map.capacity()); 211 MapCheck(map, msize >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map size %zu unexpected!", msize); 212 MapCheck(map, mcap >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity %zu unexpected!", mcap); 213 MapCheck(map, mcap >= msize, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity %zu < map size %zu!", mcap, msize); 214 PetscFunctionReturn(PETSC_SUCCESS); 215 } 216 217 PetscErrorCode check_size_capacity_coherent(map_type &map, std::size_t expected_size, std::size_t expected_min_capacity) const noexcept 218 { 219 PetscFunctionBegin; 220 PetscCall(check_size_capacity_coherent(map)); 221 MapCheck(map, map.size() == expected_size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map size %zu did not increase (from %zu) after insertion!", map.size(), expected_size); 222 MapCheck(map, map.capacity() >= expected_min_capacity, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity %zu did not increase (from %zu)!", map.capacity(), expected_min_capacity); 223 PetscFunctionReturn(PETSC_SUCCESS); 224 } 225 226 PetscErrorCode test_insert(map_type &map) noexcept 227 { 228 auto key = key_type{}; 229 auto value = mapped_type{}; 230 auto size_before = map.size(); 231 auto capacity_before = map.capacity(); 232 233 const auto check_all_reinsert = [&](value_type &key_value) { 234 using insert_return_type = std::pair<typename map_type::iterator, bool>; 235 auto &key = key_value.first; 236 auto &value = key_value.second; 237 const auto key_const = key; 238 const auto value_const = value; 239 const auto pair = std::make_pair(key_const, value_const); 240 const auto check_reinsert = [&](const char op[], const insert_return_type &ret) { 241 PetscFunctionBegin; 242 MapCheck(map, !ret.second, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s reinserted key '%s'", op, this->key_printer(key)); 243 MapCheck(map, ret.first->first == key, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s returned iterator key '%s' != expected '%s'", this->key_printer(ret.first->first), op, this->key_printer(key)); 244 MapCheck(map, ret.first->second == value, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s returned iterator value '%s' != expected '%s'", op, this->value_printer(ret.first->second), this->value_printer(value)); 245 MapCheck(map, map[key] == value, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s map[%s] '%s' != '%s'", op, this->key_printer(key), this->value_printer(map[key]), this->value_printer(value)); 246 MapCheck(map, map[key_const] == value_const, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s changed value '%s' != expected '%s'", op, this->value_printer(map[key_const]), this->value_printer(value_const)); 247 PetscFunctionReturn(PETSC_SUCCESS); 248 }; 249 250 PetscFunctionBegin; 251 #define CHECK_REINSERT(...) check_reinsert(PetscStringize(__VA_ARGS__), __VA_ARGS__) 252 // check the following operations don't clobber values 253 PetscCall(CHECK_REINSERT(map.emplace(key, value))); 254 PetscCall(CHECK_REINSERT(map.emplace(std::piecewise_construct, std::make_tuple(key), std::make_tuple(value)))); 255 PetscCall(CHECK_REINSERT(map.insert(std::make_pair(key, value)))); 256 PetscCall(CHECK_REINSERT(map.insert(pair))); 257 #undef CHECK_REINSERT 258 PetscFunctionReturn(PETSC_SUCCESS); 259 }; 260 261 PetscFunctionBegin; 262 PetscCall(this->check_size_capacity_coherent(map)); 263 // put key in map 264 PetscCallCXX(map[key] = value); 265 // check we properly sized up 266 PetscCall(this->check_size_capacity_coherent(map, size_before + 1, capacity_before)); 267 // and that the value matches 268 MapCheck(map, map[key] == value, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map default key %s != map value %s", this->key_printer(key), this->value_printer(value)); 269 // and that the following operations don't clobber the value 270 { 271 value_type kv{key, value}; 272 273 PetscCall(check_all_reinsert(kv)); 274 } 275 276 // test that clearing workings 277 capacity_before = map.capacity(); 278 PetscCall(map.clear()); 279 // should have size = 0 (but capacity unchanged) 280 PetscCall(this->check_size_capacity_coherent(map, 0, capacity_before)); 281 282 // test that all inserted values are found in the map 283 const auto test_map_contains_expected_items = [&](std::function<PetscErrorCode(std::vector<value_type> &)> fill_map, std::size_t kv_size) { 284 auto key_value_pairs = this->make_key_values(kv_size); 285 std::vector<std::size_t> found_key_value(key_value_pairs.size()); 286 287 PetscFunctionBegin; 288 PetscCall(map.clear()); 289 PetscCall(this->check_size_capacity_coherent(map, 0, 0)); 290 PetscCall(fill_map(key_value_pairs)); 291 // map size should exactly match the size of the vector, but we don't care about capacity 292 PetscCall(this->check_size_capacity_coherent(map, key_value_pairs.size(), 0)); 293 294 // sort the vector so we can use std::binary_search on it 295 PetscCallCXX(std::sort(key_value_pairs.begin(), key_value_pairs.end())); 296 for (auto it = map.cbegin(); it != map.cend(); ++it) { 297 const auto kv_begin = key_value_pairs.cbegin(); 298 const auto found = std::lower_bound(kv_begin, key_value_pairs.cend(), *it); 299 const auto dist = std::distance(kv_begin, found); 300 301 // check that the value returned exists in our expected range 302 MapCheck(map, found != key_value_pairs.cend(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map contained key-value pair (%s, %s) not present in input range!", this->key_printer(it->first), this->value_printer(it->second)); 303 MapCheck(map, dist >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Index of found key-value pair (%s -> %s) %td is < 0", this->key_printer(it->first), this->value_printer(it->second), static_cast<std::ptrdiff_t>(dist)); 304 // record that we found this particular entry 305 PetscCallCXX(++found_key_value.at(static_cast<std::size_t>(dist))); 306 } 307 308 // there should only be 1 instance of each key-value in the map 309 for (std::size_t i = 0; i < found_key_value.size(); ++i) { 310 MapCheck(map, found_key_value[i] == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map failed to insert key %s (value %s), have find count %zu", this->key_printer(key_value_pairs[i].first), this->value_printer(key_value_pairs[i].second), found_key_value[i]); 311 } 312 PetscFunctionReturn(PETSC_SUCCESS); 313 }; 314 315 // clang-format off 316 PetscCall( 317 test_map_contains_expected_items( 318 [&](std::vector<value_type> &key_value_pairs) 319 { 320 PetscFunctionBegin; 321 for (auto &&key_value : key_value_pairs) { 322 PetscCallCXX(map[key_value.first] = key_value.second); 323 PetscCall(check_all_reinsert(key_value)); 324 } 325 PetscFunctionReturn(PETSC_SUCCESS); 326 }, 327 108 328 ) 329 ); 330 // clang-format on 331 332 // test that inserting using std algorithms work 333 { 334 value_type saved_value; 335 336 // clang-format off 337 PetscCall( 338 test_map_contains_expected_items( 339 [&](std::vector<value_type> &key_value_pairs) 340 { 341 PetscFunctionBegin; 342 // save this for later 343 PetscCallCXX(saved_value = key_value_pairs.front()); 344 // test the algorithm insert works as expected 345 PetscCallCXX(std::copy(key_value_pairs.cbegin(), key_value_pairs.cend(), std::inserter(map, map.begin()))); 346 PetscFunctionReturn(PETSC_SUCCESS); 347 }, 348 179 349 ) 350 ); 351 // clang-format on 352 auto it = map.find(saved_value.first); 353 354 // can't use map[] since that might inadvertently insert it 355 MapCheck(map, it != map.end(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map failed no longer contains key-value pair (%s -> %s) after std::copy() and container went out of scope", this->key_printer(saved_value.first), this->value_printer(saved_value.second)); 356 MapCheck(map, it->first == saved_value.first, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map founnd iterator key (%s) does not match expected key (%s) after std::copy() insertion", this->key_printer(it->first), this->key_printer(saved_value.first)); 357 MapCheck(map, it->second == saved_value.second, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map founnd iterator value (%s) does not match expected value (%s) after std::copy() insertion", this->value_printer(it->second), this->value_printer(saved_value.second)); 358 } 359 PetscFunctionReturn(PETSC_SUCCESS); 360 } 361 362 PetscErrorCode test_insert() noexcept 363 { 364 map_type map; 365 366 PetscFunctionBegin; 367 PetscCall(test_insert(map)); 368 PetscFunctionReturn(PETSC_SUCCESS); 369 } 370 371 PetscErrorCode test_find(map_type &map) noexcept 372 { 373 PetscFunctionBegin; 374 { 375 const auto sample_values = this->make_key_values(145); 376 377 map = map_type(sample_values.begin(), sample_values.end()); 378 for (auto &&kv : sample_values) { 379 auto &&key = kv.first; 380 auto &&value = kv.second; 381 auto it = map.find(key); 382 383 MapCheck(map, it != map.end(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Failed to find %s in map", this->key_printer(key)); 384 MapCheck(map, it->first == key, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Find iterator key %s != expected %s", this->key_printer(it->first), this->key_printer(key)); 385 MapCheck(map, it->second == value, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Find iterator value %s != expected %s", this->value_printer(it->second), this->value_printer(value)); 386 MapCheck(map, map.contains(key), PETSC_COMM_SELF, PETSC_ERR_PLIB, "map.contains(key) reports false, even though map.find(key) successfully found it! key: %s", this->key_printer(key)); 387 MapCheck(map, map.count(key) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "map.count(%s) %zu != 1", this->key_printer(key), map.count(key)); 388 389 { 390 const auto range = map.equal_range(key); 391 const auto &range_begin = range.first; 392 const auto range_size = std::distance(range_begin, range.second); 393 394 MapCheck(map, range_size == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map equal_range() returned a range of size %zu != 1", range_size); 395 MapCheck(map, range_begin->first == key, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Equal range iterator key %s != expected %s", this->key_printer(range_begin->first), this->key_printer(key)); 396 MapCheck(map, range_begin->second == value, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Equal range iterator value %s != expected %s", this->value_printer(range_begin->second), this->value_printer(value)); 397 } 398 } 399 } 400 PetscFunctionReturn(PETSC_SUCCESS); 401 } 402 403 PetscErrorCode test_find() noexcept 404 { 405 map_type map; 406 407 PetscFunctionBegin; 408 PetscCall(test_find(map)); 409 PetscFunctionReturn(PETSC_SUCCESS); 410 } 411 412 PetscErrorCode test_erase(map_type &map) noexcept 413 { 414 auto sample_values = this->make_key_values(57); 415 const map_type backup(sample_values.cbegin(), sample_values.cend()); 416 const auto check_map_is_truly_empty = [&](map_type &map) { 417 PetscFunctionBegin; 418 MapCheck(map, map.size() == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Erasing map via iterator range didn't work, map has size %zu", map.size()); 419 MapCheck(map, map.empty(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Erasing map via iterators didn't work, map is not empty, has size %zu", map.size()); 420 // this loop should never actually fire! 421 for (auto it = map.begin(); it != map.end(); ++it) MapCheck(map, false, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Erasing via iterator range did not work, map.begin() != map.end()%s", ""); 422 PetscFunctionReturn(PETSC_SUCCESS); 423 }; 424 425 PetscFunctionBegin; 426 PetscCallCXX(map = backup); 427 // test single erase from iterator works 428 { 429 const auto it = map.begin(); 430 const auto begin_key = it->first; 431 const auto begin_val = it->second; 432 433 PetscCallCXX(map.erase(it)); 434 for (auto &&kv : map) MapCheck(map, kv.first != begin_key, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Erasing %s did not work, found again in map", this->key_printer(begin_key)); 435 // reinsert the value 436 PetscCallCXX(map[begin_key] = begin_val); 437 } 438 439 // test erase from iterator 440 for (auto it = map.begin(); it != map.end(); ++it) { 441 const auto before = it; 442 443 PetscCallCXX(map.erase(it)); 444 MapCheck(map, before == it, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Iterator changed during erase%s", ""); 445 MapCheck(map, map.occupied(before) == false, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Iterator (%s -> %s) occupied after erase", this->key_printer(before->first), this->value_printer(before->second)); 446 } 447 448 // test erase from iterator range 449 PetscCall(check_map_is_truly_empty(map)); 450 PetscCallCXX(map = backup); 451 PetscCallCXX(map.erase(map.begin(), map.end())); 452 PetscCall(check_map_is_truly_empty(map)); 453 454 // test erase by clear 455 PetscCallCXX(map = backup); 456 PetscCall(map.clear()); 457 PetscCall(check_map_is_truly_empty(map)); 458 459 { 460 std::size_t cap_before; 461 462 PetscCallCXX(map = backup); 463 cap_before = map.capacity(); 464 PetscCall(map.clear()); 465 PetscCall(check_map_is_truly_empty(map)); 466 MapCheck(map, map.capacity() == cap_before, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity decreased on clear(), capacity before %zu != current capacity %zu", cap_before, map.capacity()); 467 PetscCall(map.shrink_to_fit()); 468 MapCheck(map, map.capacity() == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity should be 0 (have %zu) after clear() -> shrink_to_fit()!", map.capacity()); 469 PetscCall(check_map_is_truly_empty(map)); 470 } 471 472 // test that clear works OK (used to be a bug when inserting after clear) 473 PetscCallCXX(map.insert(generator())); 474 PetscCallCXX(map.insert(generator())); 475 PetscCallCXX(map.insert(generator())); 476 PetscCallCXX(map.insert(generator())); 477 PetscCallCXX(map.erase(map.begin(), map.end())); 478 PetscCall(check_map_is_truly_empty(map)); 479 480 // test erase by member function swapping with empty map 481 for (auto &&kv : sample_values) PetscCallCXX(map.emplace(kv.first, kv.second)); 482 { 483 map_type alt; 484 485 // has the effect of clearing the map 486 PetscCallCXX(map.swap(alt)); 487 } 488 PetscCall(check_map_is_truly_empty(map)); 489 490 // test erase by std::swap with empty map 491 PetscCallCXX(map = backup); 492 { 493 using std::swap; 494 map_type alt; 495 496 // has the effect of clearing the map 497 PetscCallCXX(swap(map, alt)); 498 } 499 PetscCall(check_map_is_truly_empty(map)); 500 501 // test erase by key, use new values to change it up 502 sample_values = this->make_key_values(); 503 std::copy(sample_values.cbegin(), sample_values.cend(), std::inserter(map, map.begin())); 504 for (auto &&kv : sample_values) PetscCallCXX(map.erase(kv.first)); 505 PetscCall(check_map_is_truly_empty(map)); 506 PetscFunctionReturn(PETSC_SUCCESS); 507 } 508 509 PetscErrorCode test_erase() noexcept 510 { 511 map_type map; 512 513 PetscFunctionBegin; 514 PetscCall(test_erase(map)); 515 PetscFunctionReturn(PETSC_SUCCESS); 516 } 517 518 // stupid dummy function because auto-lambdas are C++14 519 template <typename It> 520 PetscErrorCode test_iterators(map_type &map, It it, It it2) noexcept 521 { 522 constexpr std::size_t max_iter = 10000; 523 constexpr auto is_normal = std::is_same<It, typename map_type::iterator>::value; 524 constexpr auto is_const = std::is_same<It, typename map_type::const_iterator>::value; 525 static_assert(is_normal || is_const, ""); 526 constexpr const char *it_name = is_normal ? "Non-const" : "Const"; 527 528 PetscFunctionBegin; 529 MapCheck(map, it == it2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator does not equal itself?", it_name); 530 PetscCallCXX(++it); 531 PetscCallCXX(it2++); 532 MapCheck(map, it == it2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator does not equal itself after ++it, and it2++", it_name); 533 PetscCallCXX(--it); 534 PetscCallCXX(it2--); 535 MapCheck(map, it == it2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator does not equal itself after --it, and it2--", it_name); 536 MapCheck(map, map.size() < max_iter, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Forward progress test only works properly if the map size (%zu) < %zu", map.size(), max_iter); 537 // check that the prefix and postfix increment and decerement make forward progress 538 { 539 std::size_t i; 540 541 // increment 542 PetscCallCXX(it = map.begin()); 543 for (i = 0; i < max_iter; ++i) { 544 if (it == map.end()) break; 545 PetscCallCXX(++it); 546 } 547 MapCheck(map, i < max_iter, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator did not appear to make forward progress using prefix increment! Reached maximum iteration count %zu for map of size %zu", it_name, max_iter, map.size()); 548 PetscCallCXX(it = map.begin()); 549 for (i = 0; i < max_iter; ++i) { 550 if (it == map.end()) break; 551 PetscCallCXX(it++); 552 } 553 MapCheck(map, i < max_iter, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator did not appear to make forward progress using postfix increment! Reached maximum iteration count %zu for map of size %zu", it_name, max_iter, map.size()); 554 555 // decrement 556 PetscCallCXX(it = std::prev(map.end())); 557 for (i = 0; i < max_iter; ++i) { 558 if (it == map.begin()) break; 559 PetscCallCXX(--it); 560 } 561 MapCheck(map, i < max_iter, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator did not appear to make forward progress using prefix decrement! Reached maximum iteration count %zu for map of size %zu", it_name, max_iter, map.size()); 562 PetscCallCXX(it = std::prev(map.end())); 563 for (i = 0; i < max_iter; ++i) { 564 if (it == map.begin()) break; 565 PetscCallCXX(it--); 566 } 567 MapCheck(map, i < max_iter, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator did not appear to make forward progress using postfix decrement! Reached maximum iteration count %zu for map of size %zu", it_name, max_iter, map.size()); 568 } 569 570 PetscFunctionReturn(PETSC_SUCCESS); 571 } 572 573 PetscErrorCode test_misc() noexcept 574 { 575 const auto sample_values = this->make_key_values(97); 576 map_type map(sample_values.begin(), sample_values.end()); 577 578 PetscFunctionBegin; 579 PetscCall(this->test_iterators(map, map.begin(), map.begin())); 580 PetscCall(this->test_iterators(map, map.cbegin(), map.cbegin())); 581 { 582 const auto backup = map; 583 auto map_copy = map; 584 const auto check_original_map_did_not_change = [&](const char op[]) { 585 PetscFunctionBegin; 586 // the original map should not have changed at all 587 MapCheck(map, map == backup, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map does not equal the original map after %s", op); 588 PetscFunctionReturn(PETSC_SUCCESS); 589 }; 590 591 MapCheck(map_copy, map == map_copy, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Copy of map does not equal the original map%s", ""); 592 PetscCall(check_original_map_did_not_change("move assign")); 593 // test that the copied map works OK 594 PetscCall(this->test_insert(map_copy)); 595 PetscCall(check_original_map_did_not_change("test_insert()")); 596 PetscCall(this->test_find(map_copy)); 597 PetscCall(check_original_map_did_not_change("test_find()")); 598 PetscCall(this->test_erase(map_copy)); 599 PetscCall(check_original_map_did_not_change("test_erase()")); 600 PetscCallCXX(map_copy = map); 601 602 auto moved_copy = std::move(map_copy); 603 604 MapCheck(moved_copy, map == moved_copy, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Moved copy of map does not equal the original map%s", ""); 605 PetscCall(check_original_map_did_not_change("move assign")); 606 PetscCall(this->test_insert(moved_copy)); 607 PetscCall(check_original_map_did_not_change("test_insert()")); 608 PetscCall(this->test_find(moved_copy)); 609 PetscCall(check_original_map_did_not_change("test_find()")); 610 PetscCall(this->test_erase(moved_copy)); 611 PetscCall(check_original_map_did_not_change("test_erase()")); 612 } 613 PetscFunctionReturn(PETSC_SUCCESS); 614 } 615 616 PetscErrorCode test() noexcept 617 { 618 PetscFunctionBegin; 619 PetscCall(this->test_insert()); 620 PetscCall(this->test_find()); 621 PetscCall(this->test_erase()); 622 PetscCall(this->test_misc()); 623 PetscFunctionReturn(PETSC_SUCCESS); 624 } 625 626 private: 627 PETSC_NODISCARD std::vector<value_type> make_key_values(std::size_t size = 100) const noexcept 628 { 629 std::vector<value_type> v(size); 630 631 std::generate(v.begin(), v.end(), this->generator); 632 return v; 633 } 634 }; 635 #ifdef PETSC_GCC_LAMBDA_VISIBILITY_WORKAROUND 636 #pragma GCC visibility pop 637 #endif 638 639 template <typename... T, typename... Args> 640 PETSC_NODISCARD static MapTester<T...> make_tester(PetscViewer vwr, const char name[], Args &&...args) 641 { 642 return {vwr, name, std::forward<Args>(args)...}; 643 } 644 645 int main(int argc, char *argv[]) 646 { 647 PetscViewer vwr; 648 PetscRandom rand; 649 650 PetscFunctionBeginUser; 651 PetscCall(PetscInitialize(&argc, &argv, nullptr, help)); 652 PetscCall(PetscRandomCreate(PETSC_COMM_SELF, &rand)); 653 PetscCall(PetscRandomSetInterval(rand, INT_MIN, INT_MAX)); 654 PetscCall(PetscRandomSetFromOptions(rand)); 655 PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &vwr)); 656 657 { 658 // printer functions 659 const auto int_printer = [](int key, std::string &buf) { 660 PetscFunctionBegin; 661 PetscCallCXX(buf = std::to_string(key)); 662 PetscFunctionReturn(PETSC_SUCCESS); 663 }; 664 const auto double_printer = [](double value, std::string &buf) { 665 PetscFunctionBegin; 666 PetscCallCXX(buf = std::to_string(value)); 667 PetscFunctionReturn(PETSC_SUCCESS); 668 }; 669 const auto foo_printer = [](const Foo &key, std::string &buf) { 670 PetscFunctionBegin; 671 PetscCall(key.to_string(buf)); 672 PetscFunctionReturn(PETSC_SUCCESS); 673 }; 674 const auto bar_printer = [](const Bar &value, std::string &buf) { 675 PetscFunctionBegin; 676 PetscCall(value.to_string(buf)); 677 PetscFunctionReturn(PETSC_SUCCESS); 678 }; 679 const auto pair_printer = [](const std::pair<int, double> &value, std::string &buf) { 680 PetscFunctionBegin; 681 PetscCallCXX(buf = '<' + std::to_string(value.first) + ", " + std::to_string(value.second) + '>'); 682 PetscFunctionReturn(PETSC_SUCCESS); 683 }; 684 685 // generator functions 686 const auto make_int = [&] { 687 PetscReal x = 0.; 688 689 PetscFunctionBegin; 690 PetscCallAbort(PETSC_COMM_SELF, PetscRandomGetValueReal(rand, &x)); 691 PetscFunctionReturn(static_cast<int>(x)); 692 }; 693 const auto make_double = [&] { 694 PetscReal x = 0.; 695 696 PetscFunctionBegin; 697 PetscCallAbort(PETSC_COMM_SELF, PetscRandomGetValueReal(rand, &x)); 698 PetscFunctionReturn(static_cast<double>(x)); 699 }; 700 const auto make_foo = [&] { 701 PetscFunctionBegin; 702 auto ret = Foo{make_int(), make_double()}; 703 PetscFunctionReturn(ret); 704 }; 705 const auto make_bar = [&] { 706 constexpr std::size_t max_size = 14, min_size = 1; 707 const auto isize = std::abs(make_int()); 708 std::vector<int> x(std::max(static_cast<std::size_t>(isize) % max_size, min_size)); 709 710 PetscFunctionBegin; 711 PetscCallCXXAbort(PETSC_COMM_SELF, std::generate(x.begin(), x.end(), make_int)); 712 auto ret = Bar{std::move(x), std::to_string(isize)}; 713 PetscFunctionReturn(ret); 714 }; 715 716 const auto int_double_generator = [&] { return std::make_pair(make_int(), make_double()); }; 717 PetscCall(make_tester<int, double>(vwr, "int-double basic map", int_printer, double_printer, int_double_generator).test()); 718 PetscCall(make_tester<int, double, BadHash>(vwr, "int-double bad hash map", int_printer, double_printer, int_double_generator).test()); 719 720 const auto int_foo_generator = [&] { return std::make_pair(make_int(), make_foo()); }; 721 PetscCall(make_tester<int, Foo, BadHash>(vwr, "int-foo bad hash map", int_printer, foo_printer, int_foo_generator).test()); 722 723 const auto foo_bar_generator = [&] { return std::make_pair(make_foo(), make_bar()); }; 724 PetscCall(make_tester<Foo, Bar>(vwr, "foo-bar basic map", foo_printer, bar_printer, foo_bar_generator).test()); 725 PetscCall(make_tester<Foo, Bar, BadHash>(vwr, "foo-bar bad hash map", foo_printer, bar_printer, foo_bar_generator).test()); 726 727 // these test that the indirect_hasher and indirect_equals classes don't barf, since the 728 // value_type of the map and hashers is both the same thing 729 const auto pair_pair_generator = [&] { 730 auto pair = std::make_pair(make_int(), make_double()); 731 return std::make_pair(pair, pair); 732 }; 733 PetscCall(make_tester<std::pair<int, double>, std::pair<int, double>>(vwr, "pair<int, double>-pair<int, double> basic map", pair_printer, pair_printer, pair_pair_generator).test()); 734 PetscCall(make_tester<std::pair<int, double>, std::pair<int, double>, BadHash>(vwr, "pair<int, double>-pair<int, double> bad hash map", pair_printer, pair_printer, pair_pair_generator).test()); 735 } 736 737 PetscCall(PetscRandomDestroy(&rand)); 738 PetscCall(PetscFinalize()); 739 return 0; 740 } 741 742 /*TEST 743 744 test: 745 suffix: umap_0 746 747 TEST*/ 748