¿Cómo se puede usar boost:: serialization con std:: shared_ptr desde C++ 11?
c++11 shared-ptr (7)
A partir de Boost 1.56, la biblioteca de serialización tiene soporte integrado para std :: shared_ptr. No necesita implementar sus propias funciones auxiliares de serialización si puede usar una versión más reciente de la biblioteca.
Sé que hay un módulo de Boost para la serialization de boost::shared_ptr
, pero no puedo encontrar nada para std::shared_ptr
.
Además, no sé cómo implementarlo fácilmente. Me temo que el siguiente código
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
no funciona De hecho, si algún objeto fue referido varias veces, se cargaría con la primera ejecución de ar>>r
, y después solo se copiará un puntero. Sin embargo, creamos múltiples objetos shared_ptr
apuntando a él, y por lo tanto lo destruiremos más de una vez.
Alguna idea sobre eso?
Algunos detalles técnicos sobre el sistema que estoy usando:
- Sistema operativo: Ubuntu 11.10 (x64)
- Compilador: g ++ (Ubuntu / Linaro 4.6.1-9ubuntu3) 4.6.1
- Versión boost: 1.46.1 (instalado con
sudo apt-get install libboost-dev
)
Esta es la mejora de la solución de denim , que admite la carga de shared_ptr que apunta a la misma memoria, pero con diferentes tipos. Este problema puede aparecer cuando archive contiene al mismo tiempo shared_ptr y shared_ptr que apuntan al mismo objeto, donde A se hereda de 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);
}
}}
Como una debilidad de esta realización, un mapa masivo.
Este es el resultado de rodar el suyo basado en el encabezado del puntero compartido <boost/serialization/shared_ptr.hpp>
por ejemplo, basado en <boost/serialization/shared_ptr.hpp>
.
Simplemente copie y pegue a continuación en un archivo de encabezado e inclúyalo:
#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
Puede ver las diferencias a <boost/serialization/shared_ptr.hpp>
here
Básicamente,
- renombrado incluye guardia
-
boost::shared_ptr
modificadoboost::shared_ptr
tostd::shared_ptr
- incluido
<memory>
lugar de<boost/shared_ptr.hpp>
- protegido
null_deleter
de la redefinición en caso de que también utiliceboost::shared_ptr
- eliminado
BOOST_SERIALIZATION_SHARED_PTR_132_HPP
: ¿de qué se trata?
Hasta ahora, esto parece estar funcionando bien.
Hola chicos, finalmente encontré una solución sobre cómo serializar std :: shared_ptr usando la serialización boost. Todo lo que necesita es la siguiente pieza de código (explicación a continuación):
#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);
}
}}
Este código simplemente serializa el objeto administrado por std :: shared_ptr en la función save (). Si las instancias de std :: shared_ptr múltiples apuntan a la misma serialización de refuerzo de objetos, se tendrá cuidado automáticamente de almacenarla solo una vez. La magia ocurre en load () donde la serialización boost devuelve un puntero sin formato al objeto (datos). Este puntero sin formato se busca en un hash que contiene un weak_ptr para cada puntero sin formato. En caso de que el weak_ptr en el hash haya expirado, podemos crear de forma segura una nueva instancia de shared_ptr, dejar que administre el puntero sin procesar y almacenar un weak_ptr en el hash. En caso de que weak_ptr no caduque, simplemente lo bloqueamos para devolver un shared_ptr. De esta forma, el recuento de referencias es correcto.
La serialización es proporcionada por boost y no por la biblioteca estándar y aunque shared_ptr
está incluido en el estándar, es parte de TR1 (informe técnico 1).
TR1 a partir de ahora no tiene serialización. Así que recomendaría que use el puntero compartido de boost.
Las versiones recientes de Boost Serialization incluyen soporte para todos los punteros inteligentes de biblioteca estándar.
No has dicho qué significa "no funciona"; no compila? No carga / almacena el valor correctamente? No ... ¿qué?
Hay dos problemas que puedo identificar aquí, aunque uno puede ser parte de tu diseño intencional.
El primero, no has hecho un puntero correcto en el procedimiento de carga. Vamos a descomponerlo:
inline void serialize(Archive & ar, std::shared_ptr<T> &t, const unsigned int version) {
if (1) { //unimportant
T* r;
ar >> r;
t = r;
}
}
Cuando crea un objeto de std :: shared_ptr, está instanciando una plantilla de clase para proporcionar una capacidad similar a un puntero (como usted sabe). Si lo hizo con un int, funcionará como un puntero int. Sin embargo, simplemente pasar el tipo como T NO significa que un puntero creado de ese tipo usará automáticamente esa plantilla; de hecho, estás creando un puntero desnudo con T * r. También puede ser int * r. Entonces no puedes inicializarlo con nuevo; r podría estar apuntando a cualquier parte. Si se intializó correctamente con una nueva, PUEDE obtener el recuento correcto de referencias para la creación / eliminación de ese objeto; esta es un área donde std :: shared_ptr no me parece que valga la pena el esfuerzo. Creo que la asignación de un puntero descubierto cuenta como la segunda referencia, no la primera, pero puedo estar equivocado. De todos modos, ese no es el problema. Probablemente estás corrompiendo el montón; un compilador debería escupir una advertencia sobre el uso de un puntero no inicializado, es una maravilla que no. Espero que no tengas advertencias desactivadas.
Si recuerdo correctamente, esa declaración de r debe ser reemplazada por:
std::shared_ptr<T> r = new std::shared_ptr<T>;
Aunque puede ser
std::shared_ptr<T> r = new std::shared_ptr<T>(r());
No he usado shared_ptr por un tiempo.
TR1, por cierto, ha estado fuera por al menos 2 años. Se basa en el shared_ptr de boost. No sé por qué estás usando Boost 1.46, pero creo que ya no estaba disponible cuando shared_ptr se convirtió en parte del estándar. Entonces, ¿debería ser compatible ...?
De todos modos, el segundo error potencial viene con
t = r;
Estoy asumiendo - incorrectamente? - que DESEA disminuir el recuento de referencias a t reasignándolo (y posiblemente destruyendo el objeto t puntos). Si quisieras copiarlo, por supuesto usarías:
*t = *r;
y asegúrese de que su constructor de copias funcione correctamente.