How to specify class version of a templated class?
Is this possible to do? I cannot find any documentation on it, nor an example. My class is very simple:
template <int N>
struct Prior {
public:
bool is_set = false;
double value[N];
private:
friend class cereal::access;
template <class Archive>
void serialize(Archive& ar, const std::uint32_t version) {
ar(is_set, value);
}
};
CEREAL_CLASS_VERSION(Prior, 1);
This will not compile with the latest cereal release. Is there a way to specify to cereal that the class is templated so that I can use the class version?
Prior is not a class
Struct and Class are nearly equivalent in c++ other than public vs private defaults (and inheritence).
Changing this to a class does not work and gives exactly the same errors.
If you wanna get really technical, then, Prior is neither a class nor a struct
Unfortunately, CEREAL_CLASS_VERSION needs a full type, which Prior is not (it's only a template definition). You can get this to work by manually defining CEREAL_CLASS_VERSION(Prior<1>, 1); /* ... */ CEREAL_CLASS_VERSION(Prior<99>, 1);, etc but I doubt that's very helpful for you.
The easiest solution would be to look at the boost preprocessor library to loop over as many N as you think you'll need with a macro and define the above.
You might be able to find some template trickery to define a cereal::detail::Version that is further templated on your N, but I can't see how to do that immediately.
As has been said you won't be able to use `CEREAL_CLASS_VERSION' directly, but you can do it manually and probably make your own macro to make this convenient.
See the following example:
template <class T>
struct A
{
T x;
template <class Archive>
void serialize( Archive & ar, const std::uint32_t version )
{
ar( x );
}
};
namespace cereal { namespace detail {
template <class T> struct Version<A<T>>
{
static const std::uint32_t version;
static std::uint32_t registerVersion()
{
::cereal::detail::StaticObject<Versions>::getInstance().mapping.emplace(
std::type_index(typeid(A<T>)).hash_code(), 3 );
return 3;
}
static void unused() { (void)version; }
}; /* end Version */
template <class T>
const std::uint32_t Version<A<T>>::version =
Version<A<T>>::registerVersion();
} } // end namespaces
The above gives a version of 3 to any instantiation of A<class T>.
@randvoorhies and @AzothAmmo Thanks for the helpful responses! That is more or less what I figured but good to confirm it. And doubly thanks for providing a workaround
@AzothAmmo Have you tried this? Does the Version<A<T>>::version actually get instantiated to trigger the registerVersion call when the compilation unit is loaded? I didn't think the compiler would actually do that for a templated version
Seemed to work in a really simple test case:
int main()
{
{
cereal::JSONOutputArchive ar( std::cout );
A<int> a = {3};
A<double> b = {3.14};
ar( a, b );
}
}
{
"value0": {
"cereal_class_version": 3,
"value0": 3
},
"value1": {
"cereal_class_version": 3,
"value0": 3.14
}
}
That's amazing! Just tested it with the Prior
I would suggest that a macro (or whatever is appropriate) for this gets incorporated into the main repo if possible. Seems pretty useful!
I just created my own macro based on the CEREAL_CLASS_VERSION macro, with some "CEREAL_UNPACK" magic courtesy of this post to support multiple template arguments
#include <cereal/cereal.hpp>
#define CEREAL_UNPACK(...) __VA_ARGS__
#define CEREAL_TEMPLATE_CLASS_VERSION(ARGS, TYPE, VERSION_NUMBER) \
namespace cereal { namespace detail { \
template<CEREAL_UNPACK ARGS> struct Version<CEREAL_UNPACK TYPE> { \
static std::uint32_t registerVersion() \
{ \
::cereal::detail::StaticObject<Versions>::getInstance().mapping.emplace( \
std::type_index(typeid(CEREAL_UNPACK TYPE)).hash_code(), \
CEREAL_UNPACK VERSION_NUMBER); \
return CEREAL_UNPACK VERSION_NUMBER; \
} \
static inline const std::uint32_t version = registerVersion(); \
CEREAL_UNUSED_FUNCTION \
}; /* end Version */ \
} }
You can use it like this:
CEREAL_TEMPLATE_CLASS_VERSION((typename KEY, typename VALUE), (std::map<KEY, VALUE>), (123));
Note the parenthesis to make CEREAL_UNPACK work!
Edit: added support for multiple template arguments Edit2: moved namespace into macro, added template arguments as ARGS macro parameter, VERSION_NUMBER now supports templates with multiple args, too (e.g. for Foo<X, Y>::Version)
maybe the following is another workaround:
class B {
int m_a;
double m_b;
static constexpr std::uint32_t sm_version = 1;
friend class cereal::access;
void serialize(auto &ar)
{
// a workaround for templated class version
auto version = sm_version;
ar(version);
if (version == 0) {
ar(m_a);
} else if (version == sm_version) {
ar(m_a, m_b);
}
}
};