Wednesday 15 July 2015

c++ - Odd behavior using boost serialization, std::tr1::unordered_map, and a custom key -



c++ - Odd behavior using boost serialization, std::tr1::unordered_map, and a custom key -

i've been investigating unusual behavior regarding boost serialization of std::tr1::unordered_map custom key. between serializing key , serializing unordered_map contains key, there 4 different situations 4 members: deserialized key, deserialized unordered_map, original key, original unordered_map

using original key original unordered_map using deserialized key deserialized unordered_map using deserialized key original unordered_map using original key deserialized unordered_map

the first 2 cases work expect, lastly 2 cases not map correctly. i've created minimum working illustration below. note need boost header unordered_map serializable. i've attached @ bottom.

#include <cstdlib> #include <unordered_map> #include <string> #include <fstream> #include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include "unordered_map.hpp" class hashkey { public: hashkey() = default; hashkey(const hashkey& orig) = default; virtual ~hashkey() = default; friend class boost::serialization::access; template<class archive> void serialize(archive & ar, const unsigned int version) { ar & const_cast<unsigned long &>(id); } inline bool operator==(const hashkey& key) const { homecoming this->id == key.id; } struct keyhasher { std::size_t operator()(const hashkey* key) const { homecoming boost::hash<unsigned long>()(key->id); } }; private: static unsigned long int idcounter; const unsigned long int id = hashkey::idcounter; }; unsigned long int hashkey::idcounter = 0; int main(int argc, char** argv) { std::tr1::unordered_map<hashkey*,std::string,hashkey::keyhasher> map; hashkey key; map[&key]="works!"; { std::ofstream ofs("key.save"); boost::archive::text_oarchive oa(ofs); oa << key; oa << map; } hashkey newkey; std::tr1::unordered_map<hashkey*,std::string,hashkey::keyhasher> newmap; { std::ifstream ifs("key.save"); boost::archive::text_iarchive ia(ifs); ia >> newkey; ia >> newmap; } std::cout<<"result: "<<map[&key]<<"\n"; std::cout<<"result: "<<newmap[&newkey]<<"\n"; std::cout<<"result: "<<map[&newkey]<<"\n"; std::cout<<"result: "<<newmap[&key]<<"\n"; homecoming 0; }

the output of code when run is:

result: works! result: works! result: result:

i not understand why lastly 2 cases not working. checked values output hash function, , correct. suspect has operator()==for pointers beingness used keys, i'm not how check. able utilize 4 cases in code. illumination on why isn't working? thanks.

this unordered_map.hpp used serialize hashmap. comes this boost ticket. include mwe:

#ifndef boost_serialization_unordered_map_hpp #define boost_serialization_unordered_map_hpp // ms compatible compilers back upwards #pragma 1 time #if defined(_msc_ver) && (_msc_ver >= 1020) # pragma 1 time #endif /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8 // serialization/unordered_map.hpp: // serialization stl unordered_map templates // (c) copyright 2002 robert ramey - http://www.rrsd.com . // use, modification , distribution subject boost software // license, version 1.0. (see accompanying file license_1_0.txt or re-create @ // http://www.boost.org/license_1_0.txt) // see http://www.boost.org updates, documentation, , revision history. #include <boost/tr1/unordered_map.hpp> #include <boost/config.hpp> #include <boost/serialization/utility.hpp> #include <boost/serialization/unordered_collections_save_imp.hpp> #include <boost/serialization/unordered_collections_load_imp.hpp> #include <boost/serialization/split_free.hpp> namespace boost { namespace serialization { namespace stl { // map input template<class archive, class container> struct archive_input_unordered_map { inline void operator()( archive &ar, container &s, const unsigned int v ){ typedef boost_deduced_typename container::value_type type; detail::stack_construct<archive, type> t(ar, v); // borland fails silently w/o total namespace ar >> boost::serialization::make_nvp("item", t.reference()); std::pair<boost_deduced_typename container::const_iterator, bool> result = s.insert(t.reference()); // note: next presumes map::value_type not tracked // in archive. usual case, here there no way // determine that. if(result.second){ ar.reset_object_address( & (result.first->second), & t.reference().second ); } } }; // multimap input template<class archive, class container> struct archive_input_unordered_multimap { inline void operator()( archive &ar, container &s, const unsigned int v ){ typedef boost_deduced_typename container::value_type type; detail::stack_construct<archive, type> t(ar, v); // borland fails silently w/o total namespace ar >> boost::serialization::make_nvp("item", t.reference()); boost_deduced_typename container::const_iterator result = s.insert(t.reference()); // note: next presumes map::value_type not tracked // in archive. usual case, here there no way // determine that. ar.reset_object_address( & result->second, & t.reference() ); } }; } // stl template< class archive, class key, class hashfcn, class equalkey, class allocator > inline void save( archive & ar, const std::tr1::unordered_map< key, hashfcn, equalkey, allocator > &t, const unsigned int /*file_version*/ ){ boost::serialization::stl::save_unordered_collection< archive, std::tr1::unordered_map< key, hashfcn, equalkey, allocator > >(ar, t); } template< class archive, class key, class hashfcn, class equalkey, class allocator > inline void load( archive & ar, std::tr1::unordered_map< key, hashfcn, equalkey, allocator > &t, const unsigned int /*file_version*/ ){ boost::serialization::stl::load_unordered_collection< archive, std::tr1::unordered_map< key, hashfcn, equalkey, allocator >, boost::serialization::stl::archive_input_unordered_map< archive, std::tr1::unordered_map< key, hashfcn, equalkey, allocator > > >(ar, t); } // split non-intrusive serialization function fellow member separate // non intrusive save/load fellow member functions template< class archive, class key, class hashfcn, class equalkey, class allocator > inline void serialize( archive & ar, std::tr1::unordered_map< key, hashfcn, equalkey, allocator > &t, const unsigned int file_version ){ boost::serialization::split_free(ar, t, file_version); } // unordered_multimap template< class archive, class key, class hashfcn, class equalkey, class allocator > inline void save( archive & ar, const std::tr1::unordered_multimap< key, hashfcn, equalkey, allocator > &t, const unsigned int /*file_version*/ ){ boost::serialization::stl::save_unordered_collection< archive, std::tr1::unordered_multimap< key, hashfcn, equalkey, allocator > >(ar, t); } template< class archive, class key, class hashfcn, class equalkey, class allocator > inline void load( archive & ar, std::tr1::unordered_multimap< key, hashfcn, equalkey, allocator > &t, const unsigned int /*file_version*/ ){ boost::serialization::stl::load_unordered_collection< archive, std::tr1::unordered_multimap< key, hashfcn, equalkey, allocator >, boost::serialization::stl::archive_input_unordered_multimap< archive, std::tr1::unordered_multimap< key, hashfcn, equalkey, allocator > > >(ar, t); } // split non-intrusive serialization function fellow member separate // non intrusive save/load fellow member functions template< class archive, class key, class hashfcn, class equalkey, class allocator > inline void serialize( archive & ar, std::tr1::unordered_multimap< key, hashfcn, equalkey, allocator > &t, const unsigned int file_version ){ boost::serialization::split_free(ar, t, file_version); } } // namespace serialization } // namespace boost #endif // boost_serialization_unordered_map_hpp

so don't exclusively understand what's going on, honest, since don't see how:

newmap.contains(&newkey)

can true. since you're storing pointers keys, newkey's address shouldn't alter when deserialize it, can't perchance in new map. said, want create simpler just... not utilize pointers:

typedef std::tr1::unordered_map<hashkey, std::string, hashkey::keyhasher> maptype; hashkey key; maptype map; map[key] = "works"; // serialize stuff... hashkey newkey; maptype newmap; // deserialize stuff... assert(map.contains(key)); assert(map.contains(newkey)); assert(newmap.contains(key)); assert(newmap.contains(newkey));

this involve fixing keyhasher take hashkey& instead of hashkey*. if 2 keys equal, should interchangeable. if utilize pointers, end relying on pointer equality, unrelated value equality (p1 == p2 implies *p1 == *p2 not other way around).

if insist on having keytype hashkey*, can do:

struct pred { bool operator()(const hashkey* a, const hashkey* b) const { homecoming (*a) == (*b); } }; typedef std::tr1::unordered_map<hashkey*, std::string, hashkey::keyhasher, pred> maptype;

but i'd suggest first way.

c++ serialization boost hash unordered-map

No comments:

Post a Comment