【问题标题】:How can boost::serialization be used with std::shared_ptr from C++11?boost::serialization 如何与 C++11 中的 std::shared_ptr 一起使用?
【发布时间】:2011-12-28 06:00:30
【问题描述】:

我知道serializationboost::shared_ptr 中有a Boost module,但我找不到std::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&gt;&gt;r 时加载,之后只会复制一个指针。但是,我们会创建多个指向它的 shared_ptr 对象,因此会多次破坏它。

对此有什么想法吗?

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

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

【问题讨论】:

  • "但是我们会创建多个指向它的 shared_ptr 对象,因此会多次破坏它。"我认为这是您在序列化中应该处理的事情。没有自动化解决方案可能知道之前序列化的某些 shared_ptr(或常规指针)指向同一个对象。
  • @NicolBolas :stated goals of boost::serialization 之一是“STL 容器和其他常用模板的序列化”。 std::shared_ptr 是 C++11 标准库的一部分。这让我相信 std::shared_ptr 的序列化已经在某个地方实现(可能在 boost 库(?)或其他地方),但我找不到它。
  • C++11,顾名思义,今年问世了。事实上,该规范仅在短短几个月前完成。虽然 GCC 和 Clang 支持它的大部分,但没有什么支持一切。当然还没有完全支持;你怎么能指望它呢?即便如此,这并不能改变这样一个事实,即它不能神奇地知道在不同时间序列化的两件事实际上是指同一个对象。这对于裸指针和智能指针一样适用。由您来处理此转换。
  • @NicolBolas :好的,我看到 C++11 是新的,std::shared_ptr 可能还不被 boost 支持。但是我不能同意你的第二点:在 boost::serialization 中实现了一个非常聪明的serialization of pointers,同样的问题是 boost::shared_ptr。
  • @NicolBolas 不,boost::serialization 确实管理指向一个对象的多个指针。裸指针和智能指针。不需要魔法。

标签: c++ boost c++11 shared-ptr boost-serialization


【解决方案1】:

从 Boost 1.56 开始,序列化库有 built-in support 用于 std::shared_ptr。如果您可以使用更新版本的库,则无需实现自己的序列化辅助函数。

【讨论】:

  • 这是正确答案。只需完整:添加#include &lt;boost/serialization/shared_ptr.hpp&gt;即可使用!
  • 那么unique_ptr 呢?我是否需要将我所有的unique_ptr 更改为shared_ptr
  • 不,你不需要改变unique_ptr;它由 boost 序列化库支持。见stackoverflow.com/a/58045763/766900
【解决方案2】:

我终于找到了一个关于如何使用 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。这样引用计数就正确了。

【讨论】:

  • 我认为常规的 std::map 可能会更好。此外,weak_ptr 有可能在检查它和调用 lock() 之间过期。而是检查 lock() 的结果。尽管如此,还是投了赞成票。
  • 这段代码看起来很奇怪:archive &lt;&lt; data; 这里你只序列化指针,然后在load 中再次加载它。但是datapoints 序列化的数据在哪里???此外,静态hash 可能映射增加直到内存不足,没有删除条目...
  • 您确定hash 是必需的吗?一方面,这几乎是内存泄漏,因为即使在很长的程序中(?),哈希也会不断增加。其次,我测试了代码并且简单地让value = std::shared_ptr&lt;Type&gt;(data)(而不是if 块)工作并且(重要的是)反序列化的对象指向相同的内存,就好像存档已经处理了并发指针一样。我错过了什么?
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

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

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

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

    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;
    

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

    【讨论】:

    • 哦,一条评论。 Boost 1.46 也可能与新的 C++1x std::shared_ptr 不兼容,因为它与 std::tr1::shared_ptr 不同,我认为这可能是 Boost 1.46 所使用的。
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      这是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);
          }
      
      }}
      

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

      【讨论】:

        【解决方案7】:

        这是基于 boost 共享指针头滚动你自己的结果,例如基于&lt;boost/serialization/shared_ptr.hpp&gt;

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

        #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
        

        您可以查看&lt;boost/serialization/shared_ptr.hpp&gt;here的差异

        基本上,

        • 重命名包含守卫
        • boost::shared_ptr 更改为std::shared_ptr
        • 包含&lt;memory&gt; 而不是&lt;boost/shared_ptr.hpp&gt;
        • 保护null_deleter不被重新定义,以防你也使用boost::shared_ptr
        • 删除了BOOST_SERIALIZATION_SHARED_PTR_132_HPP - 是什么意思?

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

        【讨论】:

        • 如果你使用的boost版本不支持shared_ptr序列化,怎么可能实现ar.reset(t,r);函数?
        • 我想你误会了。这会在 1.56 之前使用 boost 序列化序列化 std::shared_ptr。由于 boost 1.56 boost 支持 std::shared_ptr 本身。
        • 但是 1.56 之前的存档不能有函数 ar.reset(t,r); 其中 t 是 shared_ptr
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-27
        • 2012-02-24
        • 2015-06-21
        • 2011-02-19
        • 2011-05-15
        相关资源
        最近更新 更多