Compare commits

..

1 Commits

Author SHA1 Message Date
Gennaro Prota 228f211423 Record the first-registered serializer's tracking flag on save
Since 1.85, a Derived object serialized through a Base pointer across
module boundaries loses its data on load: the base subobject reads back
empty. PR #287 changed save to decide whether to emit an object id from
`co.m_bos_ptr` (the first serializer instance registered for a type),
but left the tracking flag written into the class preamble taken from
`bos`, the instance the current call happens to use.

Those can differ. With `track_selectively`, two module-local serializer
singletons for one type report different `tracking()`: the one whose
pointer serializer has been registered has `serialized_as_pointer()`
true, the other false. The writer then records one flag but lays the
object out per the other, so the reader desyncs and the subobject is
misread.

Record `co.m_bos_ptr`'s flag, the same instance the id decision uses, so
save is self-consistent again, as it was before PR #287 when both sides
read `bos`. The output is byte-identical whenever `co.m_bos_ptr == &bos`
(every single-module archive); only the already-broken cross-module case
changes.

Fixes issue #329.
2026-07-21 11:21:52 +02:00
3 changed files with 3 additions and 64 deletions
+1 -2
View File
@@ -20,7 +20,6 @@
#include <optional>
#endif
#include <boost/move/utility_core.hpp>
#include <boost/serialization/item_version_type.hpp>
#include <boost/serialization/library_version_type.hpp>
#include <boost/serialization/version.hpp>
@@ -84,7 +83,7 @@ void load_impl(
}
typename OT::value_type t;
ar >> boost::serialization::make_nvp("value",t);
ot = boost::move(t);
ot = t;
}
} // detail
+2 -2
View File
@@ -255,7 +255,7 @@ basic_oarchive_impl::save_object(
if(bos.class_info()){
if( ! co.m_initialized){
ar.vsave(class_id_optional_type(co.m_class_id));
ar.vsave(tracking_type(bos.tracking(m_flags)));
ar.vsave(tracking_type(co.m_bos_ptr->tracking(m_flags)));
ar.vsave(version_type(bos.version()));
(const_cast<cobject_type &>(co)).m_initialized = true;
}
@@ -343,7 +343,7 @@ basic_oarchive_impl::save_pointer(
}
}
if(bos.class_info()){
ar.vsave(tracking_type(bos.tracking(m_flags)));
ar.vsave(tracking_type(co.m_bos_ptr->tracking(m_flags)));
ar.vsave(version_type(bos.version()));
}
(const_cast<cobject_type &>(co)).m_initialized = true;
-60
View File
@@ -2,7 +2,6 @@
// test_optional.cpp
// (C) Copyright 2004 Pavel Vozenilek
// Copyright 2026 Gennaro Prota
// Distributed under 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)
@@ -93,59 +92,6 @@ int test(){
return EXIT_SUCCESS;
}
// A move-only value type: deleted copy, defaulted move. Loading an
// optional<M> must move the deserialized value into place rather than copy
// it.
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) \
&& !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) \
&& !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
#define BOOST_SERIALIZATION_TEST_OPTIONAL_MOVE_ONLY
struct M {
int m_x;
M() : m_x(0) {}
explicit M(int x) : m_x(x) {}
M(const M &) = delete;
M & operator=(const M &) = delete;
M(M &&) = default;
M & operator=(M &&) = default;
template<class Archive>
void serialize(Archive & ar, const unsigned int /* version */){
ar & boost::serialization::make_nvp("x", m_x);
}
};
template<template<class> class Optional>
int test_move_only(){
const char * testfile = boost::archive::tmpnam(NULL);
BOOST_REQUIRE(NULL != testfile);
Optional<M> o_empty;
Optional<M> o_value(M(42));
{
test_ostream os(testfile, TEST_STREAM_FLAGS);
test_oarchive oa(os, TEST_ARCHIVE_FLAGS);
oa << boost::serialization::make_nvp("o_empty", o_empty);
oa << boost::serialization::make_nvp("o_value", o_value);
}
// start each target in the opposite state, so load must both reset and
// assign
Optional<M> o_empty_a(M(7));
Optional<M> o_value_a;
{
test_istream is(testfile, TEST_STREAM_FLAGS);
test_iarchive ia(is, TEST_ARCHIVE_FLAGS);
ia >> boost::serialization::make_nvp("o_empty", o_empty_a);
ia >> boost::serialization::make_nvp("o_value", o_value_a);
}
BOOST_CHECK(! o_empty_a);
BOOST_CHECK(static_cast<bool>(o_value_a) && 42 == o_value_a->m_x);
std::remove(testfile);
return EXIT_SUCCESS;
}
#endif // move-only support available
#include <boost/serialization/optional.hpp>
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
#include <optional>
@@ -156,11 +102,5 @@ int test_main( int /* argc */, char* /* argv */[] ){
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
test<std::optional>();
#endif
#ifdef BOOST_SERIALIZATION_TEST_OPTIONAL_MOVE_ONLY
test_move_only<boost::optional>();
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
test_move_only<std::optional>();
#endif
#endif
return EXIT_SUCCESS;
}