繁体   English   中英

使用Boost序列化,std :: tr1 :: unordered_map和自定义键的奇怪行为

[英]Odd behavior using boost serialization, std::tr1::unordered_map, and a custom key

我一直在研究有关使用自定义键对std::tr1::unordered_map进行加速序列化的一些奇怪行为。 在序列化密钥和序列化包含密钥的unordered_map ,四种成员存在4种不同的情况:反序列化密钥,反序列化unordered_map ,原始密钥,原始unordered_map

  • 使用原始键和原始unordered_map
  • 将反序列化的密钥与反序列化的unordered_map
  • 对原始unordered_map使用反序列化密钥
  • 对反序列化unordered_map使用原始密钥

前两种情况可以按预期工作,但后两种情况无法正确映射。 我在下面创建了一个最小的工作示例。 请注意,您需要boost标头才能将unordered_map序列化。 我将其附加在底部。

#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 {
        return this->id == key.id;
    }
    struct KeyHasher {
        std::size_t operator()(const HashKey* key) const {
            return 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";


    return 0;
}

运行时此代码的输出为:

Result: works!
Result: works!
Result: 
Result: 

我不明白为什么后两种情况不起作用。 我检查了哈希函数输出的值,它们是正确的。 我怀疑这与用作键的指针与operator()==有关,但是我不确定如何检查。 我希望能够在我的代码中使用所有4种情况。 关于为什么这不起作用的任何启示? 谢谢。


这是用于序列化哈希图的unordered_map.hpp 这来自这张加价票 将其包括在MWE中:

#ifndef  BOOST_SERIALIZATION_UNORDERED_MAP_HPP
#define BOOST_SERIALIZATION_UNORDERED_MAP_HPP

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// serialization/unordered_map.hpp:
// serialization for stl unordered_map templates

// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . 
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org for updates, documentation, and 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 full namespace
        ar >> boost::serialization::make_nvp("item", t.reference());
        std::pair<BOOST_DEDUCED_TYPENAME Container::const_iterator, bool> result = 
            s.insert(t.reference());
        // note: the following presumes that the map::value_type was NOT tracked
        // in the archive.  This is the usual case, but here there is no way
        // to 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 full namespace
        ar >> boost::serialization::make_nvp("item", t.reference());
        BOOST_DEDUCED_TYPENAME Container::const_iterator result 
            = s.insert(t.reference());
        // note: the following presumes that the map::value_type was NOT tracked
        // in the archive.  This is the usual case, but here there is no way
        // to 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 member into separate
// non intrusive save/load 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 member into separate
// non intrusive save/load 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

因此,老实说,我不完全了解发生了什么,因为我不知道如何:

newMap.contains(&newKey)

可以是真的。 由于您要存储指向键的指针 ,因此对序列进行反序列化时newKey的地址不应更改,因此它不可能在新映射中。 就是说,您可能想使一切变得简单的只是...不使用指针:

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));

这将涉及修复KeyHasher以采用HashKey&而不是HashKey* 如果两个键相等,则它们应该是可互换的。 如果使用指针 ,最终将依赖于指针相等,而后者完全与值相等无关( p1 == p2意味着*p1 == *p2*p1 == *p2不行)。

如果您真的坚持将KeyType设置为HashKey* ,则可以执行以下操作:

struct Pred {
    bool operator()(const HashKey* a, const HashKey* b) const {
        return (*a) == (*b);
    }
};

typedef std::tr1::unordered_map<HashKey*,
                                std::string,
                                HashKey::KeyHasher,
                                Pred> MapType;

但是我建议第一种方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM