Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 52 additions & 4 deletions include/sg14/flat_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -725,15 +725,63 @@ class flat_map {
}
}

// TODO: use the hint, here
// TODO: use more of the hint, here
template<class... Args>
iterator try_emplace(const_iterator, const Key& k, Args&&... args) {
iterator try_emplace(const_iterator hint, const Key& k, Args&&... args) {
if (empty()) {
// hint must point end()
c_.keys.emplace_back(k);
// TODO: we must make this exception-safe if the container throws
c_.values.emplace_back(static_cast<Args&&>(args)...);
return end() - 1;
}

// at least 1 element in the map

if (hint.private_impl_getkey() == c_.keys.end()) {
// check if the current last element should be ordered before k
// if yes, insert at end, else fallback to regular try_emplace because the hint is wrong
auto prev = std::prev(hint);
if (compare_(*prev.private_impl_getkey(), k)) {
c_.keys.emplace_back(k);
// TODO: we must make this exception-safe if the container throws
c_.values.emplace_back(static_cast<Args&&>(args)...);
return end() - 1;
} else {
// hint is wrong, fallback to un-hinted implementation
return try_emplace(k, static_cast<Args&&>(args)...).first;
}
}
return try_emplace(k, static_cast<Args&&>(args)...).first;
}

// TODO: use the hint, here
// TODO: use more of the hint, here
template<class... Args>
iterator try_emplace(const_iterator, Key&& k, Args&&... args) {
iterator try_emplace(const_iterator hint, Key&& k, Args&&... args) {
if (empty()) {
// hint must point end()
c_.keys.emplace_back(static_cast<Key&&>(k));
// TODO: we must make this exception-safe if the container throws
c_.values.emplace_back(static_cast<Args&&>(args)...);
return end() - 1;
}

// at least 1 element in the map

if (hint.private_impl_getkey() == c_.keys.end()) {
// check if the current last element should be ordered before k
// if yes, insert at end, else fallback to regular try_emplace because the hint is wrong
auto prev = std::prev(hint);
if (compare_(*prev.private_impl_getkey(), k)) {
c_.keys.emplace_back(static_cast<Key&&>(k));
// TODO: we must make this exception-safe if the container throws
c_.values.emplace_back(static_cast<Args&&>(args)...);
return end() - 1;
} else {
// hint is wrong, fallback to un-hinted implementation
return try_emplace(static_cast<Key&&>(k), static_cast<Args&&>(args)...).first;
}
}
return try_emplace(static_cast<Key&&>(k), static_cast<Args&&>(args)...).first;
}

Expand Down
111 changes: 111 additions & 0 deletions test/flat_map_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,117 @@ TEST(flat_map, TryEmplace)
}
}


TEST(flat_map, TryEmplaceHint)
{
sg14::flat_map<int, InstrumentedWidget> fm;
sg14::flat_map<int, InstrumentedWidget>::iterator iterator;
if (true) {
// try_emplace for a non-existent key does move-from.
InstrumentedWidget w("abc");
iterator = fm.try_emplace(fm.end(), 1, std::move(w));
assert(w.is_moved_from);
assert(iterator == fm.begin());
}
if (true) {
// try_emplace over an existing key is a no-op.
InstrumentedWidget w("def");
iterator = fm.try_emplace(fm.end(), 1, std::move(w));
assert(!w.is_moved_from);
assert(fm.size() == 1);
assert(iterator->first == 1);
assert(iterator->second.str() == "abc");
}
if (true) {
// try_emplace to non-empty with correct hint.
InstrumentedWidget w("def");
iterator = fm.try_emplace(fm.end(), 2, std::move(w));
assert(w.is_moved_from);
assert(iterator == (fm.end() - 1));
assert(fm.size() == 2);
assert(iterator->first == 2);
assert(iterator->second.str() == "def");
}
if (true) {
// try_emplace to non-empty with wrong hint.
InstrumentedWidget w("qqq");
iterator = fm.try_emplace(fm.end(), 0, std::move(w));
assert(w.is_moved_from);
assert(iterator == fm.begin());
assert(fm.size() == 3);
assert(iterator->first == 0);
assert(iterator->second.str() == "qqq");
}
if (true) {
// try_emplace to non-empty with wrong hint (not end()).
InstrumentedWidget w("qqq2");
iterator = fm.try_emplace(fm.end() - 1, -1, std::move(w));
assert(w.is_moved_from);
assert(iterator == fm.begin());
assert(fm.size() == 4);
assert(iterator->first == -1);
assert(iterator->second.str() == "qqq2");
}
}

TEST(flat_map, TryEmplaceHintConstRef)
{
sg14::flat_map<int, InstrumentedWidget> fm;
sg14::flat_map<int, InstrumentedWidget>::iterator iterator;
int key;
if (true) {
// try_emplace for a non-existent key does move-from.
InstrumentedWidget w("abc");
key = 1;
iterator = fm.try_emplace(fm.end(), key, std::move(w));
assert(w.is_moved_from);
assert(iterator == fm.begin());
}
if (true) {
// try_emplace over an existing key is a no-op.
InstrumentedWidget w("def");
iterator = fm.try_emplace(fm.end(), key, std::move(w));
assert(!w.is_moved_from);
assert(fm.size() == 1);
assert(iterator->first == 1);
assert(iterator->second.str() == "abc");
}
if (true) {
// try_emplace to non-empty with correct hint.
InstrumentedWidget w("def");
key = 2;
iterator = fm.try_emplace(fm.end(), key, std::move(w));
assert(w.is_moved_from);
assert(iterator == (fm.end() - 1));
assert(fm.size() == 2);
assert(iterator->first == 2);
assert(iterator->second.str() == "def");
}
if (true) {
// try_emplace to non-empty with wrong hint.
InstrumentedWidget w("qqq");
key = 0;
iterator = fm.try_emplace(fm.end(), key, std::move(w));
assert(w.is_moved_from);
assert(iterator == fm.begin());
assert(fm.size() == 3);
assert(iterator->first == 0);
assert(iterator->second.str() == "qqq");
}
if (true) {
// try_emplace to non-empty with wrong hint (not end()).
InstrumentedWidget w("qqq2");
key = -1;
iterator = fm.try_emplace(fm.end() - 1, key, std::move(w));
assert(w.is_moved_from);
assert(iterator == fm.begin());
assert(fm.size() == 4);
assert(iterator->first == -1);
assert(iterator->second.str() == "qqq2");
}
}


TEST(flat_map, VectorBool)
{
using FM = sg14::flat_map<bool, bool>;
Expand Down