Compare commits

...

1 Commits

Author SHA1 Message Date
Gennaro Prota b29440c90a Add BOOST_CLASS_TEMPLATE_VERSION for versioning class templates
`BOOST_CLASS_VERSION` writes a full specialization of the version trait,
so it can only version a concrete class. A class template needs a
partial specialization, which users previously had to write by hand and
which neither the tutorial nor version.hpp explained.

`BOOST_CLASS_TEMPLATE_VERSION` writes that partial specialization.

Closes issue #179.
2026-07-22 09:52:11 +02:00
4 changed files with 147 additions and 0 deletions
+15
View File
@@ -92,6 +92,21 @@ so that instead of the above, we could write:
BOOST_CLASS_VERSION(my_class, 2)
</code></pre>
which expands to the code above.
<p>
<code style="white-space: normal">BOOST_CLASS_VERSION</code> writes a full
specialization, so it applies to a concrete class. To assign a version to a
class <i>template</i> a partial specialization is needed instead, which
<code style="white-space: normal">BOOST_CLASS_TEMPLATE_VERSION</code>
provides. The template parameter list and the specialized type are each
passed parenthesized, as they normally contain commas:
<pre><code>
BOOST_CLASS_TEMPLATE_VERSION((class T, class U), (my_class&lt;T, U&gt;), 2)
</code></pre>
This assigns version 2 to every instantiation of
<code style="white-space: normal">my_class</code>. Non-type template
parameters are written the same way, for example
<code style="white-space: normal">(class T, std::size_t N)</code> paired with
<code style="white-space: normal">(my_class&lt;T, N&gt;)</code>.
<h3><a name="level">Implementation Level</a></h3>
In the same manner as the above, the "level" of implementation of serialization is
+29
View File
@@ -72,6 +72,7 @@ const int version<T>::value;
#include <boost/mpl/less.hpp>
#include <boost/mpl/comparison.hpp>
#include <boost/preprocessor/punctuation/remove_parens.hpp>
// specify the current version number for the class
// version numbers limited to 8 bits !!!
@@ -102,4 +103,32 @@ struct version<T > \
} \
}
// Specify the current version number for a class template. Unlike
// BOOST_CLASS_VERSION, which writes a full specialization, this writes a
// partial one, so it works for a template rather than a concrete class.
// The template parameter list and the specialized type are each passed
// parenthesized (they usually contain commas):
//
// BOOST_CLASS_TEMPLATE_VERSION((class T, class U), (my_class<T, U>), 2)
//
// version numbers limited to 8 bits !!!
#define BOOST_CLASS_TEMPLATE_VERSION(TEMPLATE_PARAMETERS, TYPE, N) \
namespace boost { \
namespace serialization { \
template< BOOST_PP_REMOVE_PARENS(TEMPLATE_PARAMETERS) > \
struct version< BOOST_PP_REMOVE_PARENS(TYPE) > \
{ \
typedef mpl::int_<N> type; \
typedef mpl::integral_c_tag tag; \
BOOST_STATIC_CONSTANT(int, value = version::type::value); \
BOOST_MPL_ASSERT(( \
boost::mpl::less< \
boost::mpl::int_<N>, \
boost::mpl::int_<256> \
> \
)); \
}; \
} \
}
#endif // BOOST_SERIALIZATION_VERSION_HPP
+1
View File
@@ -64,6 +64,7 @@ test-suite "serialization" :
[ test-bsl-run_files test_binary ]
[ test-bsl-run_files test_class_info_save ]
[ test-bsl-run_files test_class_info_load ]
[ test-bsl-run_files test_class_template_version ]
[ test-bsl-run_files test_bitset ]
[ test-bsl-run_files test_complex ]
[ test-bsl-run_files test_contained_class : A ]
+102
View File
@@ -0,0 +1,102 @@
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// test_class_template_version.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 BOOST_CLASS_TEMPLATE_VERSION, which assigns a serialization version
// to a class template through a partial specialization of the version trait.
#include <cstddef>
#include <cstdio>
#include <fstream>
#include <boost/config.hpp>
#if defined(BOOST_NO_STDC_NAMESPACE)
namespace std{
using ::remove;
}
#endif
#include <boost/static_assert.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/version.hpp>
#include "test_tools.hpp"
// a class template with two type parameters; serialize records the version
// it is given on load so the round trip can be checked
template<class T, class U>
struct pair_holder {
T first;
U second;
unsigned int loaded_version;
pair_holder() : first(), second(), loaded_version(0) {}
pair_holder(T f, U s) : first(f), second(s), loaded_version(0) {}
template<class Archive>
void serialize(Archive & ar, const unsigned int version){
loaded_version = version;
ar & boost::serialization::make_nvp("first", first);
ar & boost::serialization::make_nvp("second", second);
}
};
BOOST_CLASS_TEMPLATE_VERSION((class T, class U), (pair_holder<T, U>), 5)
// a class template mixing a type parameter and a non-type parameter
template<class T, std::size_t N>
struct sized_holder {
T value;
};
BOOST_CLASS_TEMPLATE_VERSION((class T, std::size_t N), (sized_holder<T, N>), 3)
// a template we do not version, to confirm the default is still 0
template<class T>
struct unversioned {
T value;
};
int test_main(int /* argc */, char * /* argv */[]){
// the macro must set the compile time version for every instantiation of
// the template, regardless of the actual arguments
BOOST_STATIC_ASSERT(
(boost::serialization::version<pair_holder<int, double> >::value == 5)
);
BOOST_STATIC_ASSERT(
(boost::serialization::version<pair_holder<char, long> >::value == 5)
);
// works for a non-type parameter too
BOOST_STATIC_ASSERT(
(boost::serialization::version<sized_holder<int, 4> >::value == 3)
);
// an unversioned template still defaults to 0
BOOST_STATIC_ASSERT(
(boost::serialization::version<unversioned<int> >::value == 0)
);
const char * testfile = boost::archive::tmpnam(NULL);
BOOST_REQUIRE(NULL != testfile);
const pair_holder<int, double> saved(7, 2.5);
{
test_ostream os(testfile, TEST_STREAM_FLAGS);
test_oarchive oa(os, TEST_ARCHIVE_FLAGS);
oa << boost::serialization::make_nvp("ph", saved);
}
pair_holder<int, double> loaded;
{
test_istream is(testfile, TEST_STREAM_FLAGS);
test_iarchive ia(is, TEST_ARCHIVE_FLAGS);
ia >> boost::serialization::make_nvp("ph", loaded);
}
BOOST_CHECK(loaded.first == saved.first);
BOOST_CHECK(loaded.second == saved.second);
// the version set by the macro must be delivered to serialize on load
BOOST_CHECK(loaded.loaded_version == 5);
std::remove(testfile);
return EXIT_SUCCESS;
}