From afecf39568d5b4bdf62002a964be2c8433627762 Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Thu, 13 Aug 2026 09:55:15 +0200 Subject: [PATCH] Make the wrapped type of archive scalars public `version_type`, `class_id_type`, `object_id_type`, `collection_size_type`, `item_version_type` and `library_version_type` each wrap one value and convert to it, but kept a private typedef to the wrapped type. The type was therefore observable, being what both conversion operators return, without being nameable, so generic code had to guess it. A `static_cast` to a wrong guess does not compile at all, and a C style cast may reinterpret the object (wrong). `tracking_type` gains the typedef it never had, and the three classes `BOOST_ARCHIVE_STRONG_TYPEDEF` derives inherit it. Fixes #326. --- include/boost/archive/basic_archive.hpp | 15 +++-- .../serialization/collection_size_type.hpp | 6 +- .../boost/serialization/item_version_type.hpp | 6 +- .../serialization/library_version_type.hpp | 6 +- test/Jamfile.v2 | 1 + test/test_public_base_type.cpp | 67 +++++++++++++++++++ 6 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 test/test_public_base_type.cpp diff --git a/include/boost/archive/basic_archive.hpp b/include/boost/archive/basic_archive.hpp index 42e8202c5..dc2eb8734 100644 --- a/include/boost/archive/basic_archive.hpp +++ b/include/boost/archive/basic_archive.hpp @@ -40,8 +40,12 @@ BOOST_ARCHIVE_VERSION(); typedef boost::serialization::library_version_type library_version_type; class version_type { -private: +public: + // The wrapped type is part of the interface, since the conversion + // operators below return it and user code has no other way to name + // it. See issue #326. The same goes for the classes which follow. typedef uint_least32_t base_type; +private: base_type t; public: // should be private - but MPI fails if it's not!!! @@ -73,8 +77,9 @@ class version_type { }; class class_id_type { -private: +public: typedef int_least16_t base_type; +private: base_type t; public: // should be private - but then can't use BOOST_STRONG_TYPE below @@ -112,8 +117,9 @@ class class_id_type { #define BOOST_SERIALIZATION_NULL_POINTER_TAG boost::archive::class_id_type(-1) class object_id_type { -private: +public: typedef uint_least32_t base_type; +private: base_type t; public: object_id_type(): t(0) {} @@ -152,7 +158,8 @@ class object_id_type { #endif struct tracking_type { - bool t; + typedef bool base_type; + base_type t; explicit tracking_type(const bool t_ = false) : t(t_) {} diff --git a/include/boost/serialization/collection_size_type.hpp b/include/boost/serialization/collection_size_type.hpp index b99900983..1f2d56d22 100644 --- a/include/boost/serialization/collection_size_type.hpp +++ b/include/boost/serialization/collection_size_type.hpp @@ -18,8 +18,12 @@ namespace serialization { //BOOST_STRONG_TYPEDEF(std::size_t, collection_size_type) class collection_size_type { -private: +public: + // The wrapped type is part of the interface, since the conversion + // operators below return it and user code has no other way to name + // it. See issue #326. typedef std::size_t base_type; +private: base_type t; public: collection_size_type(): t(0) {} diff --git a/include/boost/serialization/item_version_type.hpp b/include/boost/serialization/item_version_type.hpp index db8c9b088..66dfe9e18 100644 --- a/include/boost/serialization/item_version_type.hpp +++ b/include/boost/serialization/item_version_type.hpp @@ -23,8 +23,12 @@ namespace serialization { #endif class item_version_type { -private: +public: + // The wrapped type is part of the interface, since the conversion + // operators below return it and user code has no other way to name + // it. See issue #326. typedef unsigned int base_type; +private: base_type t; public: // should be private - but MPI fails if it's not!!! diff --git a/include/boost/serialization/library_version_type.hpp b/include/boost/serialization/library_version_type.hpp index 05be8a7ed..f0f76512e 100644 --- a/include/boost/serialization/library_version_type.hpp +++ b/include/boost/serialization/library_version_type.hpp @@ -34,8 +34,12 @@ namespace serialization { * binary archives won't be readable !!! */ class library_version_type { -private: +public: + // The wrapped type is part of the interface, since the conversion + // operators below return it and user code has no other way to name + // it. See issue #326. typedef uint_least16_t base_type; +private: base_type t; public: library_version_type(): t(0) {} diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index b79fb1576..d90e06141 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -137,6 +137,7 @@ test-suite "serialization" : [ test-bsl-run_polymorphic_files test_exported : polymorphic_base polymorphic_derived1 polymorphic_derived2 ] # should compile + [ compile test_public_base_type.cpp ] [ compile test_strong_typedef.cpp ] ; diff --git a/test/test_public_base_type.cpp b/test/test_public_base_type.cpp new file mode 100644 index 000000000..034e3d70e --- /dev/null +++ b/test/test_public_base_type.cpp @@ -0,0 +1,67 @@ +/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8 +// test_public_base_type.cpp + +// 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) + +// See http://www.boost.org for updates, documentation, and revision history. + +// The classes an archive uses for versions, identifiers, counts and flags +// each wrap a single value and convert to it. Generic code reading such a +// value has to name the wrapped type, so every one of them publishes it as +// base_type. + +// Requested by smuzaffar in +// https://github.com/boostorg/serialization/issues/326. With the typedef +// out of reach, generic code had to guess the wrapped type, and a +// static_cast to a wrong guess does not compile at all. A C style cast +// does, by reinterpreting the object rather than converting it, which is +// what GCC 14 warned about, and rightly so. Thanks for the report. + +#include +#include +#include +#include + +// What a generic reader wants to write. A static_cast selects the +// conversion operator, so nothing is reinterpreted. +template +void check(T & t){ + typename T::base_type & r = static_cast(t); + (void) r; +} + +// And the same for a generic writer, through the const conversion. +template +void check_const(const T & t){ + const typename T::base_type v = static_cast(t); + (void) v; +} + +template +void check_both(T & t){ + check(t); + check_const(t); +} + +int main(){ + boost::archive::version_type version(0); + boost::archive::class_id_type class_id(0); + boost::archive::object_id_type object_id(std::size_t(0)); + boost::archive::tracking_type tracking(false); + boost::serialization::collection_size_type collection_size(0); + boost::serialization::item_version_type item_version(0); + boost::serialization::library_version_type library_version(0); + + check_both(version); + check_both(class_id); + check_both(object_id); + check_both(tracking); + check_both(collection_size); + check_both(item_version); + check_both(library_version); + + return 0; +}