Subject: [Boost-bugs] [Boost C++ Libraries] #9601: Unable to load archives with pointer instances which contain self reference cycles in 1.55
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2014-01-22 18:06:13
#9601: Unable to load archives with pointer instances which contain self reference
cycles in 1.55
------------------------------+---------------------------
Reporter: brandon.kohn | Owner: ramey
Type: Bugs | Status: new
Milestone: To Be Determined | Component: serialization
Version: Boost 1.55.0 | Severity: Problem
Keywords: |
------------------------------+---------------------------
First the original pointer is loaded like so:
{{{
#!c++
// because the following operation could move the items
// don't use co after this
// add to list of serialized objects so that we can properly handle
// cyclic strucures
//! Note: t's value is undefined at this point. It's whatever value it had
when serialization was called.
object_id_vector.push_back(aobject(t, cid));
// remember that that the address of these elements could change
// when we make another call so don't use the address
bpis_ptr->load_object_ptr(
ar,
t,
m_pending.version
);
BOOST_ASSERT(NULL != t);
//! Note: The object_id_vector element's address is not updated until the
load is complete.
[1] object_id_vector[ui].address = t;
}}}
load_object_ptr will eventually lead to:
{{{
#!c++
template<class Archive, class T>
BOOST_DLLEXPORT void pointer_iserializer<Archive, T>::load_object_ptr(
basic_iarchive & ar,
void * & x,
const unsigned int file_version
) const
{
...
//! This serializes an instance of T
ar_impl >> boost::serialization::make_nvp(NULL, * t);
}
}}}
If t contains a cyclic reference to itself
then the following is called:
{{{
#!c++
// extra line to evade borland issue
const bool tracking = co.tracking_level;
// if we're tracking and the pointer has already been read
if(tracking && ! track(ar, t))
// we're done
return bpis_ptr;
}}}
track will check if the oid is already loaded
and assign the address of the previously loaded pointer.
{{{
#!c++
bool
basic_iarchive_impl::track(
basic_iarchive & ar,
void * & t
){
...
// if its a reference to a old object
if(object_id_type(object_id_vector.size()) > oid){
// we're done
//! Note this line: ***
[2] t = object_id_vector[oid].address;
return false;
}
return true;
}
}}}
Recall back at the original scope [1]:
remember that that the address of these elements could change
{{{
#!c++
// when we make another call so don't use the address
bpis_ptr->load_object_ptr(
ar,
t,
m_pending.version
);
BOOST_ASSERT(NULL != t);
//! The assignment to object_id_vector[ui].address takes place after the
cycle is loaded.
//! This means means the value assigned to t at [2] is undefined.
object_id_vector[ui].address = t;
}}}
Here's a unit test that illustrates the issue:
{{{
#!c++
namespace TestPointerSerializationReferenceCycle
{
struct PointerHolder
{
PointerHolder(PointerHolder* ptr = nullptr)
: Ptr(ptr)
{}
PointerHolder* Ptr;
template <typename Archive>
void serialize(Archive& ar, const unsigned int v)
{
ar & Ptr;
}
};
}
BOOST_AUTO_TEST_CASE(TestPointerReferenceCycleLoad)
{
using namespace TestPointerSerializationReferenceCycle;
std::stringstream buff(std::stringstream::in |
std::stringstream::out);
//! Write it.
{
PointerHolder* pPtrHldr = new PointerHolder();
pPtrHldr->Ptr = pPtrHldr;
boost::archive::text_oarchive ar(buff);
ar & pPtrHldr;
}
//! Read it.
{
buff.seekg(0);
PointerHolder* pPtrHldr =
reinterpret_cast<PointerHolder*>(0xBAADF00D);
boost::archive::text_iarchive ar(buff);
ar & pPtrHldr;
BOOST_CHECK(pPtrHldr != nullptr);
if (pPtrHldr != nullptr)
{
BOOST_CHECK(pPtrHldr->Ptr == pPtrHldr);//! At this point
pPtrHldr->Ptr == 0xBAADFOOD.
}
}
}
}}}
-- Ticket URL: <https://svn.boost.org/trac/boost/ticket/9601> Boost C++ Libraries <http://www.boost.org/> Boost provides free peer-reviewed portable C++ source libraries.
This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:15 UTC