mirror of
https://github.com/boostorg/serialization.git
synced 2026-07-24 13:57:51 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb67164668 | |||
| 7132a62f52 | |||
| f689b360b1 |
+22
-2
@@ -70,8 +70,28 @@ class log_archive :
|
||||
<li><i>Note the</i> <code style="white-space: normal">log_archive</code> <i>between the</i> <>
|
||||
This is required so that base classes can downcast their <code style="white-space: normal">this</code> pointer
|
||||
to the most derived class. This is referred to as <b>C</b>uriously <b>R</b>ecurring
|
||||
<b>T</b>emplate <b>P</b>attern (<b>CRTP</b>) <a href="bibliography.html#11">[11]</a>.
|
||||
<b>T</b>emplate <b>P</b>attern (<b>CRTP</b>) <a href="bibliography.html#11">[11]</a>.
|
||||
It is used to implement static polymorphism.
|
||||
<li><i>The most derived class is responsible for calling</i> <code style="white-space: normal">init()</code>.
|
||||
The archive header (signature and version) is written by <code style="white-space: normal">init()</code>.
|
||||
Before Boost 1.73, the <b>CRTP</b> base called <code style="white-space: normal">init()</code> from its own
|
||||
constructor, but that downcast <code style="white-space: normal">this</code> to the most derived class
|
||||
while that class was still being constructed, which is undefined behavior. As of 1.73, the base
|
||||
classes no longer do this, so a derived archive that wants the standard header must call
|
||||
<code style="white-space: normal">init()</code> itself, in its own constructor body, after the base
|
||||
sub-objects have been constructed, honoring the <code style="white-space: normal">no_header</code> flag:
|
||||
<pre><code>
|
||||
log_archive(std::ostream & os, unsigned int flags = 0) :
|
||||
xml_oarchive_impl<log_archive>(os, flags)
|
||||
{
|
||||
if(0 == (flags & boost::archive::no_header))
|
||||
init();
|
||||
}
|
||||
</code></pre>
|
||||
The <code style="white-space: normal">log_archive</code> shown in this example passes
|
||||
<code style="white-space: normal">no_header</code>, so it has no header to write and its constructor
|
||||
body is empty; see <code style="white-space: normal">xml_oarchive</code>, or the
|
||||
<code style="white-space: normal">portable_binary_oarchive</code> example, for archives that do write one.
|
||||
<li><i>Base classes need to be explicitly given access to the derived class.</i>
|
||||
This can be done by making members public or by including friend declarations for
|
||||
the base classes.
|
||||
@@ -135,7 +155,7 @@ class log_archive :
|
||||
{
|
||||
public:
|
||||
log_archive(std::ostream & os, unsigned int flags = 0) :
|
||||
log_archive_impl<xml_oarchive>(os, flags)
|
||||
log_archive_impl<log_archive>(os, flags)
|
||||
{}
|
||||
};
|
||||
</code></pre>
|
||||
|
||||
@@ -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)
|
||||
@@ -21,13 +22,16 @@
|
||||
#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
|
||||
@@ -35,27 +39,91 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user