static const char help[] = "Tests UnorderedMap.\n"; #include #include #include // std::ostringstream #include #include #include // std::sort // ========================================================================================== // Setup // ========================================================================================== // see https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x static inline void hash_combine(std::size_t &) noexcept { } template static inline void hash_combine(std::size_t &seed, const T &v, Rest &&...rest) noexcept { std::hash hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); hash_combine(seed, std::forward(rest)...); } #define MAKE_HASHABLE(type, ...) \ namespace std \ { \ template <> \ struct hash { \ std::size_t operator()(const type &t) const noexcept \ { \ std::size_t ret = 0; \ hash_combine(ret, __VA_ARGS__); \ return ret; \ } \ }; \ } using pair_type = std::pair; MAKE_HASHABLE(pair_type, t.first, t.second) using namespace Petsc::util; struct Foo { int x{}; double y{}; constexpr Foo() noexcept = default; constexpr Foo(int x, double y) noexcept : x(x), y(y) { } bool operator==(const Foo &other) const noexcept { return x == other.x && y == other.y; } bool operator!=(const Foo &other) const noexcept { return !(*this == other); } bool operator<(const Foo &other) const noexcept { return std::tie(x, y) < std::tie(other.x, other.y); } PetscErrorCode to_string(std::string &buf) const noexcept { PetscFunctionBegin; PetscCallCXX(buf = std::to_string(x) + ", " + std::to_string(y)); PetscFunctionReturn(PETSC_SUCCESS); } friend std::ostream &operator<<(std::ostream &oss, const Foo &f) noexcept { std::string ret; PetscFunctionBegin; PetscCallAbort(PETSC_COMM_SELF, f.to_string(ret)); oss << ret; PetscFunctionReturn(oss); } }; MAKE_HASHABLE(Foo, t.x, t.y) struct Bar { std::vector x{}; std::string y{}; Bar() noexcept = default; Bar(std::vector x, std::string y) noexcept : x(std::move(x)), y(std::move(y)) { } bool operator==(const Bar &other) const noexcept { return x == other.x && y == other.y; } bool operator<(const Bar &other) const noexcept { return std::tie(x, y) < std::tie(other.x, other.y); } PetscErrorCode to_string(std::string &buf) const noexcept { PetscFunctionBegin; PetscCallCXX(buf = '<'); for (std::size_t i = 0; i < x.size(); ++i) { PetscCallCXX(buf += std::to_string(x[i])); if (i + 1 != x.size()) PetscCallCXX(buf += ", "); } PetscCallCXX(buf += ">, <" + y + '>'); PetscFunctionReturn(PETSC_SUCCESS); } friend std::ostream &operator<<(std::ostream &oss, const Bar &b) noexcept { std::string ret; PetscFunctionBegin; PetscCallAbort(PETSC_COMM_SELF, b.to_string(ret)); oss << ret; PetscFunctionReturn(oss); } }; struct BadHash { template constexpr std::size_t operator()(const T &) const noexcept { return 1; } }; template struct Printer { using signature = PetscErrorCode(const T &, std::string &); mutable std::string buffer; std::function printer; template Printer(F &&printer) noexcept : printer(std::forward(printer)) { } PETSC_NODISCARD const char *operator()(const T &value) const noexcept { PetscFunctionBegin; PetscCallAbort(PETSC_COMM_SELF, this->printer(value, this->buffer)); PetscFunctionReturn(this->buffer.c_str()); } }; #if defined(__GNUC__) // gcc 6.4 through 7.5 have a visibility bug: // // error: 'MapTester::test_insert()::::value_type&)> [with T = // ...]::' declared with greater visibility than the type of its field // 'MapTester::test_insert()::::value_type&)> [with T = // ...]::= 4)) || ((__GNUC__ == 7) && (__GNUC_MINOR__ <= 5)) #define PETSC_GCC_LAMBDA_VISIBILITY_WORKAROUND 1 #endif #endif #ifdef PETSC_GCC_LAMBDA_VISIBILITY_WORKAROUND #pragma GCC visibility push(hidden) #endif template class MapTester { public: using map_type = Petsc::UnorderedMap; using key_type = typename map_type::key_type; using value_type = typename map_type::value_type; using mapped_type = typename map_type::mapped_type; const PetscViewer vwr; const std::string map_name; Printer key_printer; Printer value_printer; std::function generator; PetscErrorCode view_map(const map_type &map) const noexcept { std::ostringstream oss; PetscFunctionBegin; PetscCallCXX(oss << std::boolalpha); PetscCallCXX(oss << "map: '" << this->map_name << "'\n"); PetscCallCXX(oss << " size: " << map.size() << '\n'); PetscCallCXX(oss << " capacity: " << map.capacity() << '\n'); PetscCallCXX(oss << " bucket count: " << map.bucket_count() << '\n'); PetscCallCXX(oss << " empty: " << map.empty() << '\n'); PetscCallCXX(oss << " flag bucket width: " << map_type::flag_bucket_width::value << '\n'); PetscCallCXX(oss << " flag pairs per bucket: " << map_type::flag_pairs_per_bucket::value << '\n'); PetscCallCXX(oss << " {\n"); for (auto &&entry : map) PetscCallCXX(oss << " key: [" << this->key_printer(entry.first) << "] -> [" << this->value_printer(entry.second) << "]\n"); PetscCallCXX(oss << " }\n"); PetscCall(PetscViewerASCIIPrintf(vwr, "%s", oss.str().c_str())); PetscFunctionReturn(PETSC_SUCCESS); } #define MapCheck(map__, cond__, comm__, ierr__, base_mess__, ...) \ do { \ if (PetscUnlikely(!(cond__))) { \ PetscCall(this->view_map(map__)); \ SETERRQ(comm__, ierr__, "%s: " base_mess__, this->map_name.c_str(), __VA_ARGS__); \ } \ } while (0) PetscErrorCode check_size_capacity_coherent(map_type &map) const noexcept { const auto msize = map.size(); const auto mcap = map.capacity(); PetscFunctionBegin; 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()); 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()); MapCheck(map, msize >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map size %zu unexpected!", msize); MapCheck(map, mcap >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity %zu unexpected!", mcap); MapCheck(map, mcap >= msize, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity %zu < map size %zu!", mcap, msize); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode check_size_capacity_coherent(map_type &map, std::size_t expected_size, std::size_t expected_min_capacity) const noexcept { PetscFunctionBegin; PetscCall(check_size_capacity_coherent(map)); 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); 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); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode test_insert(map_type &map) noexcept { auto key = key_type{}; auto value = mapped_type{}; auto size_before = map.size(); auto capacity_before = map.capacity(); const auto check_all_reinsert = [&](value_type &key_value) { using insert_return_type = std::pair; auto &key = key_value.first; auto &value = key_value.second; const auto key_const = key; const auto value_const = value; const auto pair = std::make_pair(key_const, value_const); const auto check_reinsert = [&](const char op[], const insert_return_type &ret) { PetscFunctionBegin; MapCheck(map, !ret.second, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s reinserted key '%s'", op, this->key_printer(key)); 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)); 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)); 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)); 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)); PetscFunctionReturn(PETSC_SUCCESS); }; PetscFunctionBegin; #define CHECK_REINSERT(...) check_reinsert(PetscStringize(__VA_ARGS__), __VA_ARGS__) // check the following operations don't clobber values PetscCall(CHECK_REINSERT(map.emplace(key, value))); PetscCall(CHECK_REINSERT(map.emplace(std::piecewise_construct, std::make_tuple(key), std::make_tuple(value)))); PetscCall(CHECK_REINSERT(map.insert(std::make_pair(key, value)))); PetscCall(CHECK_REINSERT(map.insert(pair))); #undef CHECK_REINSERT PetscFunctionReturn(PETSC_SUCCESS); }; PetscFunctionBegin; PetscCall(this->check_size_capacity_coherent(map)); // put key in map PetscCallCXX(map[key] = value); // check we properly sized up PetscCall(this->check_size_capacity_coherent(map, size_before + 1, capacity_before)); // and that the value matches 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)); // and that the following operations don't clobber the value { value_type kv{key, value}; PetscCall(check_all_reinsert(kv)); } // test that clearing workings capacity_before = map.capacity(); PetscCall(map.clear()); // should have size = 0 (but capacity unchanged) PetscCall(this->check_size_capacity_coherent(map, 0, capacity_before)); // test that all inserted values are found in the map const auto test_map_contains_expected_items = [&](std::function &)> fill_map, std::size_t kv_size) { auto key_value_pairs = this->make_key_values(kv_size); std::vector found_key_value(key_value_pairs.size()); PetscFunctionBegin; PetscCall(map.clear()); PetscCall(this->check_size_capacity_coherent(map, 0, 0)); PetscCall(fill_map(key_value_pairs)); // map size should exactly match the size of the vector, but we don't care about capacity PetscCall(this->check_size_capacity_coherent(map, key_value_pairs.size(), 0)); // sort the vector so we can use std::binary_search on it PetscCallCXX(std::sort(key_value_pairs.begin(), key_value_pairs.end())); for (auto it = map.cbegin(); it != map.cend(); ++it) { const auto kv_begin = key_value_pairs.cbegin(); const auto found = std::lower_bound(kv_begin, key_value_pairs.cend(), *it); const auto dist = std::distance(kv_begin, found); // check that the value returned exists in our expected range 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)); 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(dist)); // record that we found this particular entry PetscCallCXX(++found_key_value.at(static_cast(dist))); } // there should only be 1 instance of each key-value in the map for (std::size_t i = 0; i < found_key_value.size(); ++i) { 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]); } PetscFunctionReturn(PETSC_SUCCESS); }; // clang-format off PetscCall( test_map_contains_expected_items( [&](std::vector &key_value_pairs) { PetscFunctionBegin; for (auto &&key_value : key_value_pairs) { PetscCallCXX(map[key_value.first] = key_value.second); PetscCall(check_all_reinsert(key_value)); } PetscFunctionReturn(PETSC_SUCCESS); }, 108 ) ); // clang-format on // test that inserting using std algorithms work { value_type saved_value; // clang-format off PetscCall( test_map_contains_expected_items( [&](std::vector &key_value_pairs) { PetscFunctionBegin; // save this for later PetscCallCXX(saved_value = key_value_pairs.front()); // test the algorithm insert works as expected PetscCallCXX(std::copy(key_value_pairs.cbegin(), key_value_pairs.cend(), std::inserter(map, map.begin()))); PetscFunctionReturn(PETSC_SUCCESS); }, 179 ) ); // clang-format on auto it = map.find(saved_value.first); // can't use map[] since that might inadvertently insert it 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)); 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)); 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)); } PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode test_insert() noexcept { map_type map; PetscFunctionBegin; PetscCall(test_insert(map)); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode test_find(map_type &map) noexcept { PetscFunctionBegin; { const auto sample_values = this->make_key_values(145); map = map_type(sample_values.begin(), sample_values.end()); for (auto &&kv : sample_values) { auto &&key = kv.first; auto &&value = kv.second; auto it = map.find(key); MapCheck(map, it != map.end(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Failed to find %s in map", this->key_printer(key)); 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)); 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)); 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)); MapCheck(map, map.count(key) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "map.count(%s) %zu != 1", this->key_printer(key), map.count(key)); { const auto range = map.equal_range(key); const auto &range_begin = range.first; const auto range_size = std::distance(range_begin, range.second); MapCheck(map, range_size == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map equal_range() returned a range of size %zu != 1", range_size); 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)); 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)); } } } PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode test_find() noexcept { map_type map; PetscFunctionBegin; PetscCall(test_find(map)); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode test_erase(map_type &map) noexcept { auto sample_values = this->make_key_values(57); const map_type backup(sample_values.cbegin(), sample_values.cend()); const auto check_map_is_truly_empty = [&](map_type &map) { PetscFunctionBegin; 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()); 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()); // this loop should never actually fire! 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", ""); PetscFunctionReturn(PETSC_SUCCESS); }; PetscFunctionBegin; PetscCallCXX(map = backup); // test single erase from iterator works { const auto it = map.begin(); const auto begin_key = it->first; const auto begin_val = it->second; PetscCallCXX(map.erase(it)); 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)); // reinsert the value PetscCallCXX(map[begin_key] = begin_val); } // test erase from iterator for (auto it = map.begin(); it != map.end(); ++it) { const auto before = it; PetscCallCXX(map.erase(it)); MapCheck(map, before == it, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Iterator changed during erase%s", ""); 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)); } // test erase from iterator range PetscCall(check_map_is_truly_empty(map)); PetscCallCXX(map = backup); PetscCallCXX(map.erase(map.begin(), map.end())); PetscCall(check_map_is_truly_empty(map)); // test erase by clear PetscCallCXX(map = backup); PetscCall(map.clear()); PetscCall(check_map_is_truly_empty(map)); { std::size_t cap_before; PetscCallCXX(map = backup); cap_before = map.capacity(); PetscCall(map.clear()); PetscCall(check_map_is_truly_empty(map)); 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()); PetscCall(map.shrink_to_fit()); 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()); PetscCall(check_map_is_truly_empty(map)); } // test that clear works OK (used to be a bug when inserting after clear) PetscCallCXX(map.insert(generator())); PetscCallCXX(map.insert(generator())); PetscCallCXX(map.insert(generator())); PetscCallCXX(map.insert(generator())); PetscCallCXX(map.erase(map.begin(), map.end())); PetscCall(check_map_is_truly_empty(map)); // test erase by member function swapping with empty map for (auto &&kv : sample_values) PetscCallCXX(map.emplace(kv.first, kv.second)); { map_type alt; // has the effect of clearing the map PetscCallCXX(map.swap(alt)); } PetscCall(check_map_is_truly_empty(map)); // test erase by std::swap with empty map PetscCallCXX(map = backup); { using std::swap; map_type alt; // has the effect of clearing the map PetscCallCXX(swap(map, alt)); } PetscCall(check_map_is_truly_empty(map)); // test erase by key, use new values to change it up sample_values = this->make_key_values(); std::copy(sample_values.cbegin(), sample_values.cend(), std::inserter(map, map.begin())); for (auto &&kv : sample_values) PetscCallCXX(map.erase(kv.first)); PetscCall(check_map_is_truly_empty(map)); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode test_erase() noexcept { map_type map; PetscFunctionBegin; PetscCall(test_erase(map)); PetscFunctionReturn(PETSC_SUCCESS); } // stupid dummy function because auto-lambdas are C++14 template PetscErrorCode test_iterators(map_type &map, It it, It it2) noexcept { constexpr std::size_t max_iter = 10000; constexpr auto is_normal = std::is_same::value; constexpr auto is_const = std::is_same::value; static_assert(is_normal || is_const, ""); constexpr const char *it_name = is_normal ? "Non-const" : "Const"; PetscFunctionBegin; MapCheck(map, it == it2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator does not equal itself?", it_name); PetscCallCXX(++it); PetscCallCXX(it2++); MapCheck(map, it == it2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator does not equal itself after ++it, and it2++", it_name); PetscCallCXX(--it); PetscCallCXX(it2--); MapCheck(map, it == it2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator does not equal itself after --it, and it2--", it_name); 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); // check that the prefix and postfix increment and decerement make forward progress { std::size_t i; // increment PetscCallCXX(it = map.begin()); for (i = 0; i < max_iter; ++i) { if (it == map.end()) break; PetscCallCXX(++it); } 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()); PetscCallCXX(it = map.begin()); for (i = 0; i < max_iter; ++i) { if (it == map.end()) break; PetscCallCXX(it++); } 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()); // decrement PetscCallCXX(it = std::prev(map.end())); for (i = 0; i < max_iter; ++i) { if (it == map.begin()) break; PetscCallCXX(--it); } 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()); PetscCallCXX(it = std::prev(map.end())); for (i = 0; i < max_iter; ++i) { if (it == map.begin()) break; PetscCallCXX(it--); } 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()); } PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode test_misc() noexcept { const auto sample_values = this->make_key_values(97); map_type map(sample_values.begin(), sample_values.end()); PetscFunctionBegin; PetscCall(this->test_iterators(map, map.begin(), map.begin())); PetscCall(this->test_iterators(map, map.cbegin(), map.cbegin())); { const auto backup = map; auto map_copy = map; const auto check_original_map_did_not_change = [&](const char op[]) { PetscFunctionBegin; // the original map should not have changed at all MapCheck(map, map == backup, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map does not equal the original map after %s", op); PetscFunctionReturn(PETSC_SUCCESS); }; MapCheck(map_copy, map == map_copy, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Copy of map does not equal the original map%s", ""); PetscCall(check_original_map_did_not_change("move assign")); // test that the copied map works OK PetscCall(this->test_insert(map_copy)); PetscCall(check_original_map_did_not_change("test_insert()")); PetscCall(this->test_find(map_copy)); PetscCall(check_original_map_did_not_change("test_find()")); PetscCall(this->test_erase(map_copy)); PetscCall(check_original_map_did_not_change("test_erase()")); PetscCallCXX(map_copy = map); auto moved_copy = std::move(map_copy); MapCheck(moved_copy, map == moved_copy, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Moved copy of map does not equal the original map%s", ""); PetscCall(check_original_map_did_not_change("move assign")); PetscCall(this->test_insert(moved_copy)); PetscCall(check_original_map_did_not_change("test_insert()")); PetscCall(this->test_find(moved_copy)); PetscCall(check_original_map_did_not_change("test_find()")); PetscCall(this->test_erase(moved_copy)); PetscCall(check_original_map_did_not_change("test_erase()")); } PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode test() noexcept { PetscFunctionBegin; PetscCall(this->test_insert()); PetscCall(this->test_find()); PetscCall(this->test_erase()); PetscCall(this->test_misc()); PetscFunctionReturn(PETSC_SUCCESS); } private: PETSC_NODISCARD std::vector make_key_values(std::size_t size = 100) const noexcept { std::vector v(size); std::generate(v.begin(), v.end(), this->generator); return v; } }; #ifdef PETSC_GCC_LAMBDA_VISIBILITY_WORKAROUND #pragma GCC visibility pop #endif template PETSC_NODISCARD static MapTester make_tester(PetscViewer vwr, const char name[], Args &&...args) { return {vwr, name, std::forward(args)...}; } int main(int argc, char *argv[]) { PetscViewer vwr; PetscRandom rand; PetscFunctionBeginUser; PetscCall(PetscInitialize(&argc, &argv, nullptr, help)); PetscCall(PetscRandomCreate(PETSC_COMM_SELF, &rand)); PetscCall(PetscRandomSetInterval(rand, INT_MIN, INT_MAX)); PetscCall(PetscRandomSetFromOptions(rand)); PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &vwr)); { // printer functions const auto int_printer = [](int key, std::string &buf) { PetscFunctionBegin; PetscCallCXX(buf = std::to_string(key)); PetscFunctionReturn(PETSC_SUCCESS); }; const auto double_printer = [](double value, std::string &buf) { PetscFunctionBegin; PetscCallCXX(buf = std::to_string(value)); PetscFunctionReturn(PETSC_SUCCESS); }; const auto foo_printer = [](const Foo &key, std::string &buf) { PetscFunctionBegin; PetscCall(key.to_string(buf)); PetscFunctionReturn(PETSC_SUCCESS); }; const auto bar_printer = [](const Bar &value, std::string &buf) { PetscFunctionBegin; PetscCall(value.to_string(buf)); PetscFunctionReturn(PETSC_SUCCESS); }; const auto pair_printer = [](const std::pair &value, std::string &buf) { PetscFunctionBegin; PetscCallCXX(buf = '<' + std::to_string(value.first) + ", " + std::to_string(value.second) + '>'); PetscFunctionReturn(PETSC_SUCCESS); }; // generator functions const auto make_int = [&] { PetscReal x = 0.; PetscFunctionBegin; PetscCallAbort(PETSC_COMM_SELF, PetscRandomGetValueReal(rand, &x)); PetscFunctionReturn(static_cast(x)); }; const auto make_double = [&] { PetscReal x = 0.; PetscFunctionBegin; PetscCallAbort(PETSC_COMM_SELF, PetscRandomGetValueReal(rand, &x)); PetscFunctionReturn(static_cast(x)); }; const auto make_foo = [&] { PetscFunctionBegin; auto ret = Foo{make_int(), make_double()}; PetscFunctionReturn(ret); }; const auto make_bar = [&] { constexpr std::size_t max_size = 14, min_size = 1; const auto isize = std::abs(make_int()); std::vector x(std::max(static_cast(isize) % max_size, min_size)); PetscFunctionBegin; PetscCallCXXAbort(PETSC_COMM_SELF, std::generate(x.begin(), x.end(), make_int)); auto ret = Bar{std::move(x), std::to_string(isize)}; PetscFunctionReturn(ret); }; const auto int_double_generator = [&] { return std::make_pair(make_int(), make_double()); }; PetscCall(make_tester(vwr, "int-double basic map", int_printer, double_printer, int_double_generator).test()); PetscCall(make_tester(vwr, "int-double bad hash map", int_printer, double_printer, int_double_generator).test()); const auto int_foo_generator = [&] { return std::make_pair(make_int(), make_foo()); }; PetscCall(make_tester(vwr, "int-foo bad hash map", int_printer, foo_printer, int_foo_generator).test()); const auto foo_bar_generator = [&] { return std::make_pair(make_foo(), make_bar()); }; PetscCall(make_tester(vwr, "foo-bar basic map", foo_printer, bar_printer, foo_bar_generator).test()); PetscCall(make_tester(vwr, "foo-bar bad hash map", foo_printer, bar_printer, foo_bar_generator).test()); // these test that the indirect_hasher and indirect_equals classes don't barf, since the // value_type of the map and hashers is both the same thing const auto pair_pair_generator = [&] { auto pair = std::make_pair(make_int(), make_double()); return std::make_pair(pair, pair); }; PetscCall(make_tester, std::pair>(vwr, "pair-pair basic map", pair_printer, pair_printer, pair_pair_generator).test()); PetscCall(make_tester, std::pair, BadHash>(vwr, "pair-pair bad hash map", pair_printer, pair_printer, pair_pair_generator).test()); } PetscCall(PetscRandomDestroy(&rand)); PetscCall(PetscFinalize()); return 0; } /*TEST test: suffix: umap_0 output_file: output/empty.out TEST*/