Mostly solution for 137

This is option 1 for #137 which I'm quite happy with.  Works across compilation units and
shared libs on linux, need to test on windows.

As for #113, it doesn't seem to be an issue on linux so just remains to sort that out on windows.
This commit is contained in:
Shane Grant 2014-11-09 14:25:45 -08:00
parent 3189f037b7
commit 93b5073c62
5 changed files with 50 additions and 27 deletions

View File

@ -460,12 +460,17 @@ namespace cereal
@tparam T The type of the class being serialized
@param version The version number associated with it */
template <class T> inline
void registerClassVersion( const std::uint32_t version )
std::uint32_t registerClassVersion()
{
static const auto hash = std::type_index(typeid(T)).hash_code();
const auto insertResult = itsVersionedTypes.insert( hash );
const auto version =
detail::StaticObject<detail::Versions>::getInstance().find( hash, detail::Version<T>::version );
if( insertResult.second ) // insertion took place, serialize the version number
process( make_nvp<ArchiveType>("cereal_class_version", version) );
return version;
}
//! Member serialization
@ -473,8 +478,7 @@ namespace cereal
template <class T, PROCESS_IF(member_versioned_serialize)> inline
ArchiveType & processImpl(T const & t)
{
registerClassVersion<T>( detail::Version<T>::version );
access::member_serialize(*self, const_cast<T &>(t), detail::Version<T>::version);
access::member_serialize(*self, const_cast<T &>(t), registerClassVersion<T>());
return *self;
}
@ -483,8 +487,7 @@ namespace cereal
template <class T, PROCESS_IF(non_member_versioned_serialize)> inline
ArchiveType & processImpl(T const & t)
{
registerClassVersion<T>( detail::Version<T>::version );
CEREAL_SERIALIZE_FUNCTION_NAME(*self, const_cast<T &>(t), detail::Version<T>::version);
CEREAL_SERIALIZE_FUNCTION_NAME(*self, const_cast<T &>(t), registerClassVersion<T>());
return *self;
}
@ -493,8 +496,7 @@ namespace cereal
template <class T, PROCESS_IF(member_versioned_save)> inline
ArchiveType & processImpl(T const & t)
{
registerClassVersion<T>( detail::Version<T>::version );
access::member_save(*self, t, detail::Version<T>::version);
access::member_save(*self, t, registerClassVersion<T>());
return *self;
}
@ -503,8 +505,7 @@ namespace cereal
template <class T, PROCESS_IF(non_member_versioned_save)> inline
ArchiveType & processImpl(T const & t)
{
registerClassVersion<T>( detail::Version<T>::version );
CEREAL_SAVE_FUNCTION_NAME(*self, t, detail::Version<T>::version);
CEREAL_SAVE_FUNCTION_NAME(*self, t, registerClassVersion<T>());
return *self;
}
@ -513,8 +514,7 @@ namespace cereal
template <class T, PROCESS_IF(member_versioned_save_minimal)> inline
ArchiveType & processImpl(T const & t)
{
registerClassVersion<T>( detail::Version<T>::version );
self->process( access::member_save_minimal(*self, t, detail::Version<T>::version) );
self->process( access::member_save_minimal(*self, t, registerClassVersion<T>()) );
return *self;
}
@ -523,8 +523,7 @@ namespace cereal
template <class T, PROCESS_IF(non_member_versioned_save_minimal)> inline
ArchiveType & processImpl(T const & t)
{
registerClassVersion<T>( detail::Version<T>::version );
self->process( CEREAL_SAVE_MINIMAL_FUNCTION_NAME(*self, t, detail::Version<T>::version) );
self->process( CEREAL_SAVE_MINIMAL_FUNCTION_NAME(*self, t, registerClassVersion<T>()) );
return *self;
}

View File

@ -322,12 +322,6 @@ namespace cereal
namespace detail
{
// ######################################################################
//! Holds all registered version information
struct Versions
{
std::unordered_map<std::size_t, std::uint32_t> mapping;
}; // struct Versions
//! Version information class
/*! This is the base case for classes that have not been explicitly
registered */
@ -338,6 +332,18 @@ namespace cereal
// always get a version number of 0
};
//! Holds all registered version information
struct Versions
{
std::unordered_map<std::size_t, std::uint32_t> mapping;
std::uint32_t find( std::size_t hash, std::uint32_t version )
{
const auto result = mapping.emplace( hash, version );
return result.first->second;
}
}; // struct Versions
#ifdef CEREAL_FUTURE_EXPERIMENTAL
// ######################################################################
//! A class that can store any type

View File

@ -345,7 +345,7 @@ namespace cereal
#else // NOT _MSC_VER
//! Creates the appropriate bindings depending on whether the archive supports
//! saving or loading
static void instantiate();
static void instantiate() CEREAL_USED;
//! This typedef causes the compiler to instantiate this static function
typedef instantiate_function<instantiate> unused;
#endif // _MSC_VER
@ -372,6 +372,7 @@ namespace cereal
void bind(std::false_type) const
{
instantiate_polymorphic_binding((T*) 0, 0, adl_tag{});
std::cerr << "here i am " << std::endl;
}
//! Binding for abstract types

View File

@ -4,7 +4,6 @@
/*
Copyright (c) 2014, Randolph Voorhies, Shane Grant
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
@ -15,7 +14,6 @@
* Neither the name of cereal nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@ -30,6 +28,24 @@
#ifndef CEREAL_DETAILS_STATIC_OBJECT_HPP_
#define CEREAL_DETAILS_STATIC_OBJECT_HPP_
//! Prevent link optimization from removing non-referenced static objects
/*! Especially for polymorphic support, we create static objects which
may not ever be explicitly referenced. Most linkers will detect this
and remove the code causing various unpleasant runtime errors. These
macros, adopted from Boost (see force_include.hpp) prevent this
(C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
Use, modification and distribution is subject to 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) */
#ifdef _MSC_VER
# define CEREAL_DLL_EXPORT __declspec(dllexport)
# define CEREAL_USED
#else // clang or gcc
# define CEREAL_DLL_EXPORT
//# define CEREAL_USED
# define CEREAL_USED __attribute__ ((__used__))
#endif
namespace cereal
{
namespace detail
@ -38,7 +54,6 @@ namespace cereal
/*! This class will create a single copy (singleton) of some
type and ensures that merely referencing this type will
cause it to be instantiated and initialized pre-execution.
For example, this is used heavily in the polymorphic pointer
serialization mechanisms to bind various archive types with
different polymorphic classes */
@ -49,7 +64,7 @@ namespace cereal
//! Forces instantiation at pre-execution time
static void instantiate( T const & ) {}
static T & create()
CEREAL_DLL_EXPORT static T & create()
{
static T t;
instantiate(instance);
@ -59,16 +74,16 @@ namespace cereal
StaticObject( StaticObject const & /*other*/ ) {}
public:
static T & getInstance()
CEREAL_DLL_EXPORT static T & getInstance()
{
return create();
}
private:
static T & instance;
CEREAL_DLL_EXPORT static T & instance;
};
template <class T> T & StaticObject<T>::instance = StaticObject<T>::create();
template <class T> CEREAL_DLL_EXPORT T & StaticObject<T>::instance = StaticObject<T>::create();
} // namespace detail
} // namespace cereal

View File

@ -258,6 +258,8 @@ namespace cereal
auto & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
std::cerr << "o " << bindingMap.size () << std::endl;
auto binding = bindingMap.find(std::type_index(ptrinfo));
if(binding == bindingMap.end())
throw cereal::Exception("Trying to save an unregistered polymorphic type (" + cereal::util::demangle(ptrinfo.name()) + ").\n"