Ответ 1
Начиная с версии Boost 1.56, библиотека сериализации имеет встроенную поддержку для std:: shared_ptr. Вам не нужно выполнять свои собственные вспомогательные функции сериализации, если вы можете использовать более новую версию библиотеки.
Я знаю, что есть модуль Boost для serialization boost::shared_ptr
, но я ничего не могу найти для 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>>r
, после чего будет скопирован только указатель. Однако мы бы создали несколько объектов shared_ptr
, указывающих на него, и, следовательно, уничтожили бы его более одного раза.
Любые идеи по этому поводу?
Некоторые технические сведения о используемой системе:
sudo apt-get install libboost-dev
)Начиная с версии Boost 1.56, библиотека сериализации имеет встроенную поддержку для std:: shared_ptr. Вам не нужно выполнять свои собственные вспомогательные функции сериализации, если вы можете использовать более новую версию библиотеки.
Привет, ребята, я наконец нашел решение о том, как сериализовать std:: shared_ptr, используя сериализацию boost. Все, что вам нужно, это следующий фрагмент кода (объяснение следует):
#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);
}
}}
Этот код просто сериализует объект, управляемый std:: shared_ptr, в функции save(). Если несколько экземпляров std:: shared_ptr указывают на то, что сериализация одного и того же объекта будет автоматически сохранена, он будет храниться только один раз. Магия происходит в load(), где boost serialization возвращает необработанный указатель на объект (данные). Этот необработанный указатель просматривается в хеше, который содержит weak_ptr для каждого необработанного указателя. В случае, если hash_x_xr в истекшем состоянии истек, мы можем безопасно создать новый экземпляр shared_ptr, позволить ему управлять необработанным указателем и хранить в hash значение weak_ptr. В случае, если weak_ptr не истек, мы просто заблокируем его, чтобы вернуть shared_ptr. Таким образом, правильный подсчет ссылок.
Сериализация обеспечивается повышением, а не стандартной библиотекой, и хотя shared_ptr
включен в стандарт, он является частью TR1 (технический отчет 1).
TR1 на данный момент не имеет сериализации. Поэтому я бы рекомендовал вам использовать общий указатель boost.
Вы не сказали, что означает "не работает"; он не компилируется? Он не загружает/сохраняет значение правильно? Это не... что?
Есть две проблемы, которые я могу определить здесь, но, возможно, это часть вашего намеренного дизайна.
Во-первых, вы не указали правильный указатель в процедуре загрузки. Позвольте сломать его:
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. Затем вы не можете инициализировать его новым; 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 points). Если вы хотите скопировать его, вы, конечно, будете использовать:
*t = *r;
и убедитесь, что ваш конструктор копий работает правильно.
Последние версии Boost Serialization включают поддержку всех стандартных указателей стандартной библиотеки.
Это усовершенствование решения 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);
}
}}
Как слабость этой реализации - одна массивная карта.
Это результат опрокидывания на основе заголовка указателя с общим заголовком, например. на основе <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
- что бы это ни было?Пока это работает нормально.