Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 82 additions & 26 deletions include/boost/serialization/optional.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8

// (C) Copyright 2002-4 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)
Expand All @@ -21,41 +22,108 @@
#endif

#include <boost/move/utility_core.hpp>
#include <boost/core/addressof.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/serialization/item_version_type.hpp>
#include <boost/serialization/library_version_type.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/detail/is_default_constructible.hpp>
#include <boost/serialization/detail/stack_constructor.hpp>

// function specializations must be defined in the appropriate
// namespace - boost::serialization
namespace boost {
namespace serialization {
namespace detail {

// The value of an optional<T> is serialized like a single element of an
// STL container: when T is default constructible we serialize it in place,
// otherwise we route construction through save/load_construct_data so that
// types without a default constructor can be reconstructed on load. The
// default-constructible path is left untouched so that archives written by
// earlier versions of the library keep the same layout.

// save the value: T is default constructible
template<class Archive, class OT>
typename boost::enable_if<
typename detail::is_default_constructible<typename OT::value_type>,
void
>::type
save_value(Archive & ar, const OT & ot){
ar << boost::serialization::make_nvp("value", *ot);
}

// save the value: T is not default constructible
template<class Archive, class OT>
typename boost::disable_if<
typename detail::is_default_constructible<typename OT::value_type>,
void
>::type
save_value(Archive & ar, const OT & ot){
typedef typename OT::value_type value_type;
const value_type & v = *ot;
const boost::serialization::item_version_type item_version(
boost::serialization::version<value_type>::value
);
ar << BOOST_SERIALIZATION_NVP(item_version);
boost::serialization::save_construct_data_adl(
ar,
boost::addressof(v),
item_version
);
ar << boost::serialization::make_nvp("value", v);
}

// load the value: T is default constructible
template<class Archive, class OT>
typename boost::enable_if<
typename detail::is_default_constructible<typename OT::value_type>,
void
>::type
load_value(Archive & ar, OT & ot, const unsigned int version){
if(0 == version){
boost::serialization::item_version_type item_version(0);
boost::serialization::library_version_type library_version(
ar.get_library_version()
);
if(boost::serialization::library_version_type(3) < library_version){
ar >> BOOST_SERIALIZATION_NVP(item_version);
}
}
typename OT::value_type t;
ar >> boost::serialization::make_nvp("value", t);
ot = boost::move(t);
}

// load the value: T is not default constructible
template<class Archive, class OT>
typename boost::disable_if<
typename detail::is_default_constructible<typename OT::value_type>,
void
>::type
load_value(Archive & ar, OT & ot, const unsigned int /* version */){
typedef typename OT::value_type value_type;
boost::serialization::item_version_type item_version(0);
ar >> BOOST_SERIALIZATION_NVP(item_version);
detail::stack_construct<Archive, value_type> aux(ar, item_version);
ar >> boost::serialization::make_nvp("value", aux.reference());
ot = boost::move(aux.reference());
ar.reset_object_address(boost::addressof(*ot), aux.address());
}

// OT is of the form optional<T>
template<class Archive, class OT>
void save_impl(
Archive & ar,
const OT & ot
){
// It is an inherent limitation to the serialization of optional.hpp
// that the underlying type must be either a pointer or must have a
// default constructor. It's possible that this could change sometime
// in the future, but for now, one will have to work around it. This can
// be done by serialization the optional<T> as optional<T *>
#ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS
BOOST_STATIC_ASSERT(
boost::serialization::detail::is_default_constructible<typename OT::value_type>::value
|| boost::is_pointer<typename OT::value_type>::value
);
#endif
const bool tflag(ot);
ar << boost::serialization::make_nvp("initialized", tflag);
if (tflag){
ar << boost::serialization::make_nvp("value", *ot);
save_value(ar, ot);
}
}

Expand All @@ -72,19 +140,7 @@ void load_impl(
ot.reset();
return;
}

if(0 == version){
boost::serialization::item_version_type item_version(0);
boost::serialization::library_version_type library_version(
ar.get_library_version()
);
if(boost::serialization::library_version_type(3) < library_version){
ar >> BOOST_SERIALIZATION_NVP(item_version);
}
}
typename OT::value_type t;
ar >> boost::serialization::make_nvp("value",t);
ot = boost::move(t);
load_value(ar, ot, version);
}

} // detail
Expand Down
75 changes: 75 additions & 0 deletions test/test_optional.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,77 @@ int test_move_only(){
}
#endif // move-only support available

// A type without a default constructor. It is reconstructed on load through
// save_construct_data / load_construct_data, which is exactly what an
// optional<ND> now relies on (see issue #121). m_i is the constructor
// argument, m_x is ordinary serialized state.
struct ND {
int m_i;
int m_x;
ND(int i, int x) : m_i(i), m_x(x) {}
explicit ND(int i) : m_i(i), m_x(0) {}
template<class Archive>
void serialize(Archive & ar, const unsigned int /* version */){
ar & boost::serialization::make_nvp("x", m_x);
}
bool operator==(const ND & rhs) const {
return m_i == rhs.m_i && m_x == rhs.m_x;
}
};

namespace boost {
namespace serialization {

template<class Archive>
void save_construct_data(
Archive & ar, const ND * p, const unsigned int /* version */
){
ar << boost::serialization::make_nvp("i", p->m_i);
}

template<class Archive>
void load_construct_data(
Archive & ar, ND * p, const unsigned int /* version */
){
int i;
ar >> boost::serialization::make_nvp("i", i);
::new(p) ND(i);
}

} // serialization
} // boost

template<template<class> class Optional>
int test_non_default_ctor(){
const char * testfile = boost::archive::tmpnam(NULL);
BOOST_REQUIRE(NULL != testfile);

const Optional<ND> o_empty;
const Optional<ND> o_value(ND(7, 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
// reconstruct
Optional<ND> o_empty_a(ND(1, 1));
Optional<ND> 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));
BOOST_CHECK(o_value_a && *o_value == *o_value_a);

std::remove(testfile);
return EXIT_SUCCESS;
}

#include <boost/serialization/optional.hpp>
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
#include <optional>
Expand All @@ -162,5 +233,9 @@ int test_main( int /* argc */, char* /* argv */[] ){
test_move_only<std::optional>();
#endif
#endif
test_non_default_ctor<boost::optional>();
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
test_non_default_ctor<std::optional>();
#endif
return EXIT_SUCCESS;
}
Loading