From 45f9f03cc1488c5925e953fdc2ba05f626480c45 Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Wed, 29 Jul 2026 15:02:36 +0200 Subject: [PATCH] Track variant selectively so reused values round-trip `boost::variant` and `std::variant` specialized `tracking_level` to `track_always`. With object tracking on, serializing the same variant twice by value wrote the second occurrence as a bare object reference. On load, that reference could not be resolved into a separate destination: value loads receive their destination by value, not by reference, so the tracking table has nowhere to copy from, and the second variant silently loaded as empty. So, use `track_selectively` (the default for class types) for all three variant flavors: a variant is tracked when serialized through a pointer and stored by value otherwise, matching every other value type. The tracking flag is written per object in the class info, so archives written with the old track_always variant still load correctly and no archive version bump is needed. Also add a regression test that serializes a variant twice into one archive and checks that both copies load back to the original value. Closes #203. --- include/boost/serialization/variant.hpp | 4 ++-- test/test_variant.cpp | 32 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/include/boost/serialization/variant.hpp b/include/boost/serialization/variant.hpp index 41ec27520..67ec4191d 100644 --- a/include/boost/serialization/variant.hpp +++ b/include/boost/serialization/variant.hpp @@ -294,7 +294,7 @@ struct tracking_level< variant >{ typedef mpl::integral_c_tag tag; - typedef mpl::int_< ::boost::serialization::track_always> type; + typedef mpl::int_< ::boost::serialization::track_selectively> type; BOOST_STATIC_CONSTANT(int, value = type::value); }; @@ -304,7 +304,7 @@ struct tracking_level< std::variant >{ typedef mpl::integral_c_tag tag; - typedef mpl::int_< ::boost::serialization::track_always> type; + typedef mpl::int_< ::boost::serialization::track_selectively> type; BOOST_STATIC_CONSTANT(int, value = type::value); }; #endif diff --git a/test/test_variant.cpp b/test/test_variant.cpp index 47d8ba2da..52535bf29 100644 --- a/test/test_variant.cpp +++ b/test/test_variant.cpp @@ -194,6 +194,32 @@ void test(Variant & v) test_type(v); } +// Serializing the same variant twice by value must store two independent +// values, not an object reference the loader cannot resolve into a separate +// destination object. Regression test for issue #203. +template +void test_reuse(const Variant & v){ + const char * testfile = boost::archive::tmpnam(NULL); + BOOST_REQUIRE(testfile != NULL); + { + test_ostream os(testfile, TEST_STREAM_FLAGS); + test_oarchive oa(os, TEST_ARCHIVE_FLAGS); + oa << boost::serialization::make_nvp("first", v); + oa << boost::serialization::make_nvp("second", v); + } + Variant v1; + Variant v2; + { + test_istream is(testfile, TEST_STREAM_FLAGS); + test_iarchive ia(is, TEST_ARCHIVE_FLAGS); + ia >> boost::serialization::make_nvp("first", v1); + ia >> boost::serialization::make_nvp("second", v2); + } + BOOST_CHECK(are_equal(v, v1)); + BOOST_CHECK(are_equal(v, v2)); + std::remove(testfile); +} + int test_boost_variant(){ std::cerr << "Testing boost_variant\n"; boost::variant v; @@ -201,6 +227,8 @@ int test_boost_variant(){ const A a; boost::variant v1 = & a; test_type(v1); + v = 1; + test_reuse(v); return EXIT_SUCCESS; } @@ -214,6 +242,8 @@ int test_boost_variant2(){ const A a; boost::variant2::variant v1 = & a; test_type(v1); + v = 1; + test_reuse(v); return EXIT_SUCCESS; } #endif @@ -228,6 +258,8 @@ int test_std_variant(){ const A a; std::variant v1 = & a; test_type(v1); + v = 1; + test_reuse(v); return EXIT_SUCCESS; } #endif