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
15 changes: 15 additions & 0 deletions doc/traits.html
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,21 @@ <h3><a name="version">Version</a></h3>
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
Expand Down
29 changes: 29 additions & 0 deletions include/boost/serialization/version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 !!!
Expand Down Expand Up @@ -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 change: 1 addition & 0 deletions test/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]
Expand Down
102 changes: 102 additions & 0 deletions test/test_class_template_version.cpp
Original file line number Diff line number Diff line change
@@ -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;
}