diff --git a/include/cereal/types/map.hpp b/include/cereal/types/map.hpp index 329f033f..0eb78978 100644 --- a/include/cereal/types/map.hpp +++ b/include/cereal/types/map.hpp @@ -33,4 +33,55 @@ #include "cereal/types/concepts/pair_associative_container.hpp" #include +namespace cereal +{ + /** + * std::multimap must not default to the implementation in + * pair_associative_container.hpp, because it incorrectly + * handles equivalent keys when loading the map. + * + * The cause is the use of emplace_hint. + * Since it iserts the value before the hint, key order will be flipped + * + * Here we are using emplace (if key is equivalent to the previous one), + * or emplace_hint if keys are different, because this is a faster path + * + * Emplace will preserve order of equivalent keys, while where + * non-equivalent ones are placed doesn't matter. + **/ + + template + inline void load(Archive &ar, std::multimap &map) + { + size_type size; + ar(make_size_tag(size)); + + map.clear(); + + typename std::multimap::key_compare cmp; + auto hint = map.begin(); + for (size_t i = 0; i < size; ++i) + { + typename std::multimap::key_type key; + typename std::multimap::mapped_type value; + + ar(make_map_item(key, value)); + if(!cmp(hint->first, key) && !cmp(key, hint->first)) { + #ifdef CEREAL_OLDER_GCC + hint = map.insert(std::move(key), std::move(value)); + #else // NOT CEREAL_OLDER_GCC + hint = map.emplace(std::move(key), std::move(value)); + #endif // NOT CEREAL_OLDER_GCC + } else { + #ifdef CEREAL_OLDER_GCC + hint = map.insert_hint(hint, std::move(key), std::move(value)); + #else // NOT CEREAL_OLDER_GCC + hint = map.emplace_hint(hint, std::move(key), std::move(value)); + #endif // NOT CEREAL_OLDER_GCC + } + + } + } +} + #endif // CEREAL_TYPES_MAP_HPP_ diff --git a/include/cereal/types/set.hpp b/include/cereal/types/set.hpp index 0f83fbbe..7d871bd9 100644 --- a/include/cereal/types/set.hpp +++ b/include/cereal/types/set.hpp @@ -96,7 +96,34 @@ namespace cereal template inline void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::multiset & multiset ) { - set_detail::load( ar, multiset ); + size_type size; + ar( make_size_tag( size ) ); + + multiset.clear(); + + typename std::multiset::key_compare cmp; + + auto hint = multiset.begin(); + for( size_type i = 0; i < size; ++i ) + { + typename std::multiset::key_type key; + ar( key ); + + // if hint == key + if(!cmp(*hint, key) && !cmp(key, *hint)) { + #ifdef CEREAL_OLDER_GCC + hint = multiset.insert(std::move(key)); + #else // NOT CEREAL_OLDER_GCC + hint = multiset.emplace(std::move(key)); + #endif // NOT CEREAL_OLDER_GCC + }else { + #ifdef CEREAL_OLDER_GCC + hint = multiset.insert_hint( hint, std::move( key ) ); + #else // NOT CEREAL_OLDER_GCC + hint = multiset.emplace_hint( hint, std::move( key ) ); + #endif // NOT CEREAL_OLDER_GCC + } + } } } // namespace cereal