24

我知道有一个用于序列化Boost 模块,但我找不到任何.boost::shared_ptrstd::shared_ptr

另外,我不知道如何轻松实现它。恐怕下面的代码

namespace boost{namespace serialization{
template<class Archive, class T>
inline void serialize(Archive & ar, std::shared_ptr<T> &t, const unsigned int version)
{
  if(Archive::is_loading::value) {T*r;ar>>r;t=r;}
  else {ar<<t.get();}
}
}}//namespaces

不起作用。事实上,如果某个对象被多次引用,它将在第一次运行时加载ar>>r,然后只复制一个指针。然而,我们会创建多个shared_ptr指向它的对象,因此会不止一次地破坏它。

有什么想法吗?

关于我正在使用的系统的一些技术细节:

  • 操作系统:Ubuntu 11.10 (x64)
  • 编译器:g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
  • 升压版本:1.46.1(安装sudo apt-get install libboost-dev
4

7 回答 7

15

我终于找到了一个关于如何使用 boost 序列化序列化 std::shared_ptr 的解决方案。您只需要以下代码(解释如下):

#include <boost/serialization/split_free.hpp>
#include <boost/unordered_map.hpp>

//---/ Wrapper for std::shared_ptr<> /------------------------------------------

namespace boost { namespace serialization {

template<class Archive, class Type>
void save(Archive & archive, const std::shared_ptr<Type> & value, const unsigned int /*version*/)
{
    Type *data = value.get();
    archive << data;
}

template<class Archive, class Type>
void load(Archive & archive, std::shared_ptr<Type> & value, const unsigned int /*version*/)
{
    Type *data;
    archive >> data;

    typedef std::weak_ptr<Type> WeakPtr;
    static boost::unordered_map<void*, WeakPtr> hash;

    if (hash[data].expired())
    {
         value = std::shared_ptr<Type>(data);
         hash[data] = value;
    }
    else value = hash[data].lock();
}

template<class Archive, class Type>
inline void serialize(Archive & archive, std::shared_ptr<Type> & value, const unsigned int version)
{
    split_free(archive, value, version);
}

}}

此代码只是在函数 save() 中序列化由 std::shared_ptr 管理的对象。如果多个 std::shared_ptr 实例指向同一个对象 boost 序列化将自动注意只存储一次。魔法发生在 load() 中,boost 序列化返回一个指向对象(数据)的原始指针。在为每个原始指针保存一个weak_ptr 的散列中查找该原始指针。如果散列中的weak_ptr 过期,我们可以安全地创建一个新的shared_ptr 实例,让它管理原始指针并在散列中存储一个weak_ptr。如果weak_ptr 没有过期,我们只需将其锁定以返回shared_ptr。这样引用计数是正确的。

于 2013-02-21T10:04:27.883 回答
15

从 Boost 1.56 开始,序列化库内置了对 std::shared_ptr 的支持。如果您可以使用更新版本的库,则无需实现自己的序列化辅助函数。

于 2015-01-17T20:02:00.547 回答
3

序列化是由 boost 而不是标准库提供的,虽然shared_ptr它包含在标准中,但它是 TR1(技术报告 1)的一部分。

TR1 目前还没有序列化。所以我建议你使用 boost 的共享指针。

于 2012-02-17T10:18:38.880 回答
2

您还没有说过“不起作用”是什么意思;它不编译?它没有正确加载/存储值?它没有……什么?

我可以在这里确定两个问题,一个可能是您有意设计的一部分。

首先,您没有在加载过程中创建正确的指针。让我们分解一下:

inline void serialize(Archive & ar, std::shared_ptr<T> &t, const unsigned int version) {
    if (1) { //unimportant
        T* r;
        ar >> r;
        t = r;
    }
}

当您创建 std::shared_ptr 的对象时,您正在实例化一个类模板以提供类似指针的功能(如您所知)。如果您使用 int,它将作为 int 指针工作。但是,简单地将类型传递为 T 并不意味着创建该类型的指针将自动使用该模板;实际上,您正在使用 T* r 创建一个裸指针。它也可以是 int *r。然后你无法用 new 初始化它;r 可能指向任何地方。如果它用新的正确初始化,您可能会获得正确的引用计数以创建/删除该对象;这是 std::shared_ptr 对我来说似乎不值得努力的一个领域。我认为来自裸指针的赋值算作第二个引用,而不是第一个,但我可能错了?无论如何,这不是问题。您可能正在破坏堆;编译器应该发出关于使用未初始化指针的警告,奇怪的是它没有。我希望你没有关闭警告。

如果我没记错的话, r 的声明需要替换为:

std::shared_ptr<T> r = new std::shared_ptr<T>;

虽然它可能是

std::shared_ptr<T> r = new std::shared_ptr<T>(r());

我有一段时间没有使用 shared_ptr 了。

顺便说一句,TR1 已经出局至少 2 年了。它基于 boost 的 shared_ptr。我不知道您为什么要使用 Boost 1.46,但我认为它在 shared_ptr 成为标准的一部分时已经过时了?所以它应该兼容......?

无论如何,第二个潜在错误伴随着

t = r;

我假设 - 不正确?- 您希望通过重新分配它(并可能破坏 t 指向的对象)来减少对 t 的引用计数。如果你打算复制它,你当然会使用:

*t = *r;

并确保您的复制构造函数正常工作。

于 2012-01-12T15:50:54.607 回答
2

Boost Serialization 的最新版本包括对所有标准库智能指针的支持。

于 2015-11-24T23:01:10.823 回答
0

这是基于 boost 共享指针标头(例如基于<boost/serialization/shared_ptr.hpp>.

只需将以下内容复制并粘贴到头文件中并包含它:

#ifndef BOOST_SERIALIZATION_STD_SHARED_PTR_HPP
#define BOOST_SERIALIZATION_STD_SHARED_PTR_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
// shared_ptr.hpp: serialization for boost shared pointer

// (C) Copyright 2004 Robert Ramey and Martin Ecker
// 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 <cstddef> // NULL

#include <boost/config.hpp>
#include <boost/mpl/integral_c.hpp>
#include <boost/mpl/integral_c_tag.hpp>

#include <boost/detail/workaround.hpp>
#include <memory>

#include <boost/serialization/split_free.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/tracking.hpp>

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// shared_ptr serialization traits
// version 1 to distinguish from boost 1.32 version. Note: we can only do this
// for a template when the compiler supports partial template specialization

#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
    namespace boost {
    namespace serialization{
        template<class T>
        struct version< ::std::shared_ptr< T > > {
            typedef mpl::integral_c_tag tag;
            #if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3206))
            typedef BOOST_DEDUCED_TYPENAME mpl::int_<1> type;
            #else
            typedef mpl::int_<1> type;
            #endif
            #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x570))
            BOOST_STATIC_CONSTANT(int, value = 1);
            #else
            BOOST_STATIC_CONSTANT(int, value = type::value);
            #endif
        };
        // don't track shared pointers
        template<class T>
        struct tracking_level< ::std::shared_ptr< T > > {
            typedef mpl::integral_c_tag tag;
            #if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3206))
            typedef BOOST_DEDUCED_TYPENAME mpl::int_< ::boost::serialization::track_never> type;
            #else
            typedef mpl::int_< ::boost::serialization::track_never> type;
            #endif
            #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x570))
            BOOST_STATIC_CONSTANT(int, value = ::boost::serialization::track_never);
            #else
            BOOST_STATIC_CONSTANT(int, value = type::value);
            #endif
        };
    }}
    #define BOOST_SERIALIZATION_SHARED_PTR(T)
#else
    // define macro to let users of these compilers do this
    #define BOOST_SERIALIZATION_SHARED_PTR(T)                         \
    BOOST_CLASS_VERSION(                                              \
        ::std::shared_ptr< T >,                                     \
        1                                                             \
    )                                                                 \
    BOOST_CLASS_TRACKING(                                             \
        ::std::shared_ptr< T >,                                     \
        ::boost::serialization::track_never                           \
    )                                                                 \
    /**/
#endif

namespace boost {
namespace serialization{

#ifndef BOOST_SERIALIZATION_SHARED_PTR_HPP
struct null_deleter {
    void operator()(void const *) const {}
};
#endif

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// serialization for shared_ptr

template<class Archive, class T>
inline void save(
    Archive & ar,
    const std::shared_ptr< T > &t,
    const unsigned int /* file_version */
){
    // The most common cause of trapping here would be serializing
    // something like shared_ptr<int>.  This occurs because int
    // is never tracked by default.  Wrap int in a trackable type
    BOOST_STATIC_ASSERT((tracking_level< T >::value != track_never));
    const T * t_ptr = t.get();
    ar << boost::serialization::make_nvp("px", t_ptr);
}

template<class Archive, class T>
inline void load(
    Archive & ar,
    std::shared_ptr< T > &t,
    const unsigned int /*file_version*/
){
    // The most common cause of trapping here would be serializing
    // something like shared_ptr<int>.  This occurs because int
    // is never tracked by default.  Wrap int in a trackable type
    BOOST_STATIC_ASSERT((tracking_level< T >::value != track_never));
    T* r;
    ar >> boost::serialization::make_nvp("px", r);
    ar.reset(t,r);
}

template<class Archive, class T>
inline void serialize(
    Archive & ar,
    std::shared_ptr< T > &t,
    const unsigned int file_version
){
    // correct shared_ptr serialization depends upon object tracking
    // being used.
    BOOST_STATIC_ASSERT(
        boost::serialization::tracking_level< T >::value
        != boost::serialization::track_never
    );
    boost::serialization::split_free(ar, t, file_version);
}

} // namespace serialization
} // namespace boost

#endif // BOOST_SERIALIZATION_STD_SHARED_PTR_HPP

您可以在<boost/serialization/shared_ptr.hpp> 此处查看差异

基本上,

  • 重命名包含守卫
  • 改为boost::shared_ptr_std::shared_ptr
  • 包括<memory>而不是<boost/shared_ptr.hpp>
  • 防止null_deleter重新定义,以防您也使用boost::shared_ptr
  • 已删除BOOST_SERIALIZATION_SHARED_PTR_132_HPP- 无论是什么?

到目前为止,这似乎工作得很好。

于 2015-02-18T14:26:57.560 回答
0

这是对denim解决方案的改进,它支持加载指向相同内存但类型不同的 shared_ptr。当存档同时包含指向同一个对象的 shared_ptr 和 shared_ptr 时,可能会出现此问题,其中 A 是从 B 继承的。

namespace boost {
namespace serialization {

    template<class Archive, class Type>
    void save(Archive & archive, const std::shared_ptr<Type> & value, const unsigned int /*version*/)
    {
        Type *data = value.get();
        archive << data;
    }

    static std::map<void*, std::weak_ptr<void>> hash;

    template<class Archive, class Type>
    void load(Archive & archive, std::shared_ptr<Type> & value, const unsigned int /*version*/)
    {
        Type *data;
        archive >> data;

        if (hash[data].expired())
        {
            std::shared_ptr<void> ptr(data);
            value = static_pointer_cast<Type>(ptr);
            hash[data] = ptr;
        }
        else value = static_pointer_cast<Type>(hash[data].lock());
    }

    template<class Archive, class Type>
    inline void serialize(Archive & archive, std::shared_ptr<Type> & value, const unsigned int version)
    {
        split_free(archive, value, version);
    }

}}

作为这种认识的一个弱点 - 一张巨大的地图。

于 2014-12-25T22:53:22.693 回答