Skip to content

Commit b29440c

Browse files
committed
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.
1 parent 7132a62 commit b29440c

4 files changed

Lines changed: 147 additions & 0 deletions

File tree

doc/traits.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,21 @@ <h3><a name="version">Version</a></h3>
9292
BOOST_CLASS_VERSION(my_class, 2)
9393
</code></pre>
9494
which expands to the code above.
95+
<p>
96+
<code style="white-space: normal">BOOST_CLASS_VERSION</code> writes a full
97+
specialization, so it applies to a concrete class. To assign a version to a
98+
class <i>template</i> a partial specialization is needed instead, which
99+
<code style="white-space: normal">BOOST_CLASS_TEMPLATE_VERSION</code>
100+
provides. The template parameter list and the specialized type are each
101+
passed parenthesized, as they normally contain commas:
102+
<pre><code>
103+
BOOST_CLASS_TEMPLATE_VERSION((class T, class U), (my_class&lt;T, U&gt;), 2)
104+
</code></pre>
105+
This assigns version 2 to every instantiation of
106+
<code style="white-space: normal">my_class</code>. Non-type template
107+
parameters are written the same way, for example
108+
<code style="white-space: normal">(class T, std::size_t N)</code> paired with
109+
<code style="white-space: normal">(my_class&lt;T, N&gt;)</code>.
95110

96111
<h3><a name="level">Implementation Level</a></h3>
97112
In the same manner as the above, the "level" of implementation of serialization is

include/boost/serialization/version.hpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const int version<T>::value;
7272

7373
#include <boost/mpl/less.hpp>
7474
#include <boost/mpl/comparison.hpp>
75+
#include <boost/preprocessor/punctuation/remove_parens.hpp>
7576

7677
// specify the current version number for the class
7778
// version numbers limited to 8 bits !!!
@@ -102,4 +103,32 @@ struct version<T > \
102103
} \
103104
}
104105

106+
// Specify the current version number for a class template. Unlike
107+
// BOOST_CLASS_VERSION, which writes a full specialization, this writes a
108+
// partial one, so it works for a template rather than a concrete class.
109+
// The template parameter list and the specialized type are each passed
110+
// parenthesized (they usually contain commas):
111+
//
112+
// BOOST_CLASS_TEMPLATE_VERSION((class T, class U), (my_class<T, U>), 2)
113+
//
114+
// version numbers limited to 8 bits !!!
115+
#define BOOST_CLASS_TEMPLATE_VERSION(TEMPLATE_PARAMETERS, TYPE, N) \
116+
namespace boost { \
117+
namespace serialization { \
118+
template< BOOST_PP_REMOVE_PARENS(TEMPLATE_PARAMETERS) > \
119+
struct version< BOOST_PP_REMOVE_PARENS(TYPE) > \
120+
{ \
121+
typedef mpl::int_<N> type; \
122+
typedef mpl::integral_c_tag tag; \
123+
BOOST_STATIC_CONSTANT(int, value = version::type::value); \
124+
BOOST_MPL_ASSERT(( \
125+
boost::mpl::less< \
126+
boost::mpl::int_<N>, \
127+
boost::mpl::int_<256> \
128+
> \
129+
)); \
130+
}; \
131+
} \
132+
}
133+
105134
#endif // BOOST_SERIALIZATION_VERSION_HPP

test/Jamfile.v2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ test-suite "serialization" :
6464
[ test-bsl-run_files test_binary ]
6565
[ test-bsl-run_files test_class_info_save ]
6666
[ test-bsl-run_files test_class_info_load ]
67+
[ test-bsl-run_files test_class_template_version ]
6768
[ test-bsl-run_files test_bitset ]
6869
[ test-bsl-run_files test_complex ]
6970
[ test-bsl-run_files test_contained_class : A ]
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
2+
// test_class_template_version.cpp
3+
4+
// Copyright 2026 Gennaro Prota
5+
// Distributed under the Boost Software License, Version 1.0.
6+
// (See accompanying file LICENSE_1_0.txt or copy at
7+
// https://www.boost.org/LICENSE_1_0.txt)
8+
9+
// Tests BOOST_CLASS_TEMPLATE_VERSION, which assigns a serialization version
10+
// to a class template through a partial specialization of the version trait.
11+
12+
#include <cstddef>
13+
#include <cstdio>
14+
#include <fstream>
15+
16+
#include <boost/config.hpp>
17+
18+
#if defined(BOOST_NO_STDC_NAMESPACE)
19+
namespace std{
20+
using ::remove;
21+
}
22+
#endif
23+
24+
#include <boost/static_assert.hpp>
25+
#include <boost/serialization/nvp.hpp>
26+
#include <boost/serialization/version.hpp>
27+
#include "test_tools.hpp"
28+
29+
// a class template with two type parameters; serialize records the version
30+
// it is given on load so the round trip can be checked
31+
template<class T, class U>
32+
struct pair_holder {
33+
T first;
34+
U second;
35+
unsigned int loaded_version;
36+
pair_holder() : first(), second(), loaded_version(0) {}
37+
pair_holder(T f, U s) : first(f), second(s), loaded_version(0) {}
38+
template<class Archive>
39+
void serialize(Archive & ar, const unsigned int version){
40+
loaded_version = version;
41+
ar & boost::serialization::make_nvp("first", first);
42+
ar & boost::serialization::make_nvp("second", second);
43+
}
44+
};
45+
46+
BOOST_CLASS_TEMPLATE_VERSION((class T, class U), (pair_holder<T, U>), 5)
47+
48+
// a class template mixing a type parameter and a non-type parameter
49+
template<class T, std::size_t N>
50+
struct sized_holder {
51+
T value;
52+
};
53+
54+
BOOST_CLASS_TEMPLATE_VERSION((class T, std::size_t N), (sized_holder<T, N>), 3)
55+
56+
// a template we do not version, to confirm the default is still 0
57+
template<class T>
58+
struct unversioned {
59+
T value;
60+
};
61+
62+
int test_main(int /* argc */, char * /* argv */[]){
63+
// the macro must set the compile time version for every instantiation of
64+
// the template, regardless of the actual arguments
65+
BOOST_STATIC_ASSERT(
66+
(boost::serialization::version<pair_holder<int, double> >::value == 5)
67+
);
68+
BOOST_STATIC_ASSERT(
69+
(boost::serialization::version<pair_holder<char, long> >::value == 5)
70+
);
71+
// works for a non-type parameter too
72+
BOOST_STATIC_ASSERT(
73+
(boost::serialization::version<sized_holder<int, 4> >::value == 3)
74+
);
75+
// an unversioned template still defaults to 0
76+
BOOST_STATIC_ASSERT(
77+
(boost::serialization::version<unversioned<int> >::value == 0)
78+
);
79+
80+
const char * testfile = boost::archive::tmpnam(NULL);
81+
BOOST_REQUIRE(NULL != testfile);
82+
83+
const pair_holder<int, double> saved(7, 2.5);
84+
{
85+
test_ostream os(testfile, TEST_STREAM_FLAGS);
86+
test_oarchive oa(os, TEST_ARCHIVE_FLAGS);
87+
oa << boost::serialization::make_nvp("ph", saved);
88+
}
89+
pair_holder<int, double> loaded;
90+
{
91+
test_istream is(testfile, TEST_STREAM_FLAGS);
92+
test_iarchive ia(is, TEST_ARCHIVE_FLAGS);
93+
ia >> boost::serialization::make_nvp("ph", loaded);
94+
}
95+
BOOST_CHECK(loaded.first == saved.first);
96+
BOOST_CHECK(loaded.second == saved.second);
97+
// the version set by the macro must be delivered to serialize on load
98+
BOOST_CHECK(loaded.loaded_version == 5);
99+
100+
std::remove(testfile);
101+
return EXIT_SUCCESS;
102+
}

0 commit comments

Comments
 (0)