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
34 changes: 30 additions & 4 deletions include/boost/archive/detail/iserializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// iserializer.hpp: interface for serialization system.

// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
// 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 @@ -35,6 +36,7 @@ namespace std{
} // namespace std
#endif

#include <boost/core/underlying_type.hpp>
#include <boost/static_assert.hpp>

#include <boost/mpl/eval_if.hpp>
Expand All @@ -46,10 +48,12 @@ namespace std{
#ifndef BOOST_SERIALIZATION_DEFAULT_TYPE_INFO
#include <boost/serialization/extended_type_info_typeid.hpp>
#endif
#include <boost/serialization/library_version_type.hpp>
#include <boost/serialization/throw_exception.hpp>
#include <boost/serialization/smart_cast.hpp>
#include <boost/serialization/static_warning.hpp>

#include <boost/type_traits/conditional.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/type_traits/is_enum.hpp>
#include <boost/type_traits/is_const.hpp>
Expand Down Expand Up @@ -560,10 +564,32 @@ template<class Archive>
struct load_enum_type {
template<class T>
static void invoke(Archive &ar, T &t){
// convert integers to correct enum to load
int i;
ar >> boost::serialization::make_nvp(NULL, i);
t = static_cast< T >(i);
// Enumerators were always stored as int before archive library version
// 21; read that layout from any older archive so existing data keeps
// loading. From version 21 on, an enumerator is stored as an int if it
// fits in an int; otherwise as the enum's underlying type (see
// save_enum_type).
if(ar.get_library_version()
< boost::serialization::library_version_type(21)
){
int i;
ar >> boost::serialization::make_nvp(NULL, i);
t = static_cast< T >(i);
return;
}
#ifndef BOOST_NO_UNDERLYING_TYPE
typedef typename boost::underlying_type< T >::type underlying_type;
typedef typename boost::conditional<
sizeof(underlying_type) <= sizeof(int),
int,
underlying_type
>::type load_type;
#else
typedef int load_type;
#endif
load_type lt;
ar >> boost::serialization::make_nvp(NULL, lt);
t = static_cast< T >(lt);
}
};

Expand Down
26 changes: 23 additions & 3 deletions include/boost/archive/detail/oserializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// oserializer.hpp: interface for serialization system.

// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
// 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 @@ -29,6 +30,8 @@

#include <boost/config.hpp>

#include <boost/core/underlying_type.hpp>

#include <boost/static_assert.hpp>
#include <boost/detail/workaround.hpp>

Expand All @@ -46,6 +49,7 @@
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/static_warning.hpp>

#include <boost/type_traits/conditional.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/type_traits/is_enum.hpp>
#include <boost/type_traits/is_const.hpp>
Expand Down Expand Up @@ -488,9 +492,25 @@ struct save_enum_type
{
template<class T>
static void invoke(Archive &ar, const T &t){
// convert enum to integers on save
const int i = static_cast<int>(t);
ar << boost::serialization::make_nvp(NULL, i);
// Save an enumerator as an integer wide enough to hold every value of
// the enum's underlying type. An underlying type no wider than int
// keeps the historical layout (a plain int), so existing archives are
// unaffected; a wider underlying type is written at full width instead
// of being truncated to int. Archives that need the wider layout carry
// library version 21 or greater (see load_enum_type).
#ifndef BOOST_NO_UNDERLYING_TYPE
typedef typename boost::underlying_type< T >::type underlying_type;
typedef typename boost::conditional<
sizeof(underlying_type) <= sizeof(int),
int,
underlying_type
>::type save_type;
#else
// no way to query the underlying type: keep the historical int
typedef int save_type;
#endif
const save_type st = static_cast< save_type >(t);
ar << boost::serialization::make_nvp(NULL, st);
}
};

Expand Down
4 changes: 3 additions & 1 deletion src/basic_archive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,11 @@ BOOST_ARCHIVE_SIGNATURE(){
// was fully constructed.
// 19- Boost 1.76 April 2021
// 20- Boost 1.84 April 2021
// 21- enumerators are stored as int (if they fit) or as the enum's underlying
// type, instead of always as int
BOOST_SYMBOL_VISIBLE boost::serialization::library_version_type
BOOST_ARCHIVE_VERSION(){
return boost::serialization::library_version_type(20);
return boost::serialization::library_version_type(21);
}

} // namespace archive
Expand Down
1 change: 1 addition & 0 deletions test/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ test-suite "serialization" :
[ test-bsl-run_files test_derived_class_ptr : A ]
[ test-bsl-run_files test_diamond ]
[ test-bsl-run_files test_diamond_complex ]
[ test-bsl-run_files test_enum ]
[ test-bsl-run_files test_filesystem_path ]
[ test-bsl-run_files test_forward_list : A : : [ requires cxx11_hdr_forward_list ] ] # BOOST_NO_CXX11_HDR_FORWARD_LIST
[ test-bsl-run_files test_forward_list_ptrs : A : : [ requires cxx11_hdr_forward_list ] ] # BOOST_NO_CXX11_HDR_FORWARD_LIST
Expand Down
93 changes: 93 additions & 0 deletions test/test_enum.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// test_enum.cpp

// Copyright 2026 Gennaro Prota
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)

// Tests serialization of enumerators. In particular it checks that a value of
// a scoped enum whose underlying type is wider than int round trips at full
// width, rather than being truncated to int on save.

#include <cstddef>
#include <cstdio>
#include <fstream>

#include <boost/config.hpp>

#if defined(BOOST_NO_STDC_NAMESPACE)
namespace std{
using ::remove;
}
#endif

#include <boost/serialization/nvp.hpp>
#include "test_tools.hpp"

// underlying type is int, so the archived form is unchanged from earlier
// releases of the library
enum color { red, green = 3, blue = 100 };

#ifndef BOOST_NO_CXX11_SCOPED_ENUMS

// the value 2^32 does not fit in a 32 bit int; a plain cast to int on save
// used to truncate it
enum class wide : long long {
one = 1,
big = 1LL << 32
};

// a narrow underlying type: still stored through int, so unchanged on the
// wire, but it must round trip
enum class narrow : signed char {
minus_one = -1,
two = 2,
hundred = 100
};

#endif // BOOST_NO_CXX11_SCOPED_ENUMS

int test_main(int /* argc */, char * /* argv */[]){
const char * testfile = boost::archive::tmpnam(NULL);
BOOST_REQUIRE(NULL != testfile);

const color c_save = blue;
#ifndef BOOST_NO_CXX11_SCOPED_ENUMS
const wide w_save = wide::big;
const narrow n_save = narrow::minus_one;
#endif
{
test_ostream os(testfile, TEST_STREAM_FLAGS);
test_oarchive oa(os, TEST_ARCHIVE_FLAGS);
oa << boost::serialization::make_nvp("c", c_save);
#ifndef BOOST_NO_CXX11_SCOPED_ENUMS
oa << boost::serialization::make_nvp("w", w_save);
oa << boost::serialization::make_nvp("n", n_save);
#endif
}
color c_load = red;
#ifndef BOOST_NO_CXX11_SCOPED_ENUMS
wide w_load = wide::one;
narrow n_load = narrow::two;
#endif
{
test_istream is(testfile, TEST_STREAM_FLAGS);
test_iarchive ia(is, TEST_ARCHIVE_FLAGS);
ia >> boost::serialization::make_nvp("c", c_load);
#ifndef BOOST_NO_CXX11_SCOPED_ENUMS
ia >> boost::serialization::make_nvp("w", w_load);
ia >> boost::serialization::make_nvp("n", n_load);
#endif
}
BOOST_CHECK(c_save == c_load);
#ifndef BOOST_NO_CXX11_SCOPED_ENUMS
BOOST_CHECK(w_save == w_load);
// the point of the test: the value survived without being truncated
BOOST_CHECK(static_cast<long long>(w_load) == (1LL << 32));
BOOST_CHECK(n_save == n_load);
#endif

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