Skip to content

Commit

Permalink
C++17: use inline globals for StaticObjects
Browse files Browse the repository at this point in the history
This prevents multiple definition errors in Clang,
and also stops dllexporting functions with internal
linkage.  Degrades gracefully when C++17 is not
present.

Fix USCiLab#595
Fix USCiLab#652
Fix USCiLab#582
Fix USCiLab#643
  • Loading branch information
InBetweenNames committed Oct 28, 2020
1 parent 562321c commit 06696d0
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 7 deletions.
2 changes: 1 addition & 1 deletion include/cereal/archives/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ namespace cereal
void saveLong(T lu){ saveValue( static_cast<std::uint64_t>( lu ) ); }

public:
#ifdef _MSC_VER
#if defined(_MSC_VER) && _MSC_VER < 1916
//! MSVC only long overload to current node
void saveValue( unsigned long lu ){ saveLong( lu ); };
#else // _MSC_VER
Expand Down
22 changes: 22 additions & 0 deletions include/cereal/cereal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,26 @@ namespace cereal
Interfaces for other forms of serialization functions is similar. This
macro should be placed at global scope.
@ingroup Utility */

//! On C++17, define the StaticObject as inline to merge the definitions across TUs
//! This prevents multiple definition errors when this macro appears in a header file
//! included in multiple TUs.
#ifdef CEREAL_HAS_CPP17
#define CEREAL_CLASS_VERSION(TYPE, VERSION_NUMBER) \
namespace cereal { namespace detail { \
template <> struct Version<TYPE> \
{ \
static std::uint32_t registerVersion() \
{ \
::cereal::detail::StaticObject<Versions>::getInstance().mapping.emplace( \
std::type_index(typeid(TYPE)).hash_code(), VERSION_NUMBER ); \
return VERSION_NUMBER; \
} \
static inline const std::uint32_t version = registerVersion(); \
CEREAL_UNUSED_FUNCTION \
}; /* end Version */ \
} } // end namespaces
#else
#define CEREAL_CLASS_VERSION(TYPE, VERSION_NUMBER) \
namespace cereal { namespace detail { \
template <> struct Version<TYPE> \
Expand All @@ -275,6 +295,8 @@ namespace cereal
Version<TYPE>::registerVersion(); \
} } // end namespaces

#endif

// ######################################################################
//! The base output archive class
/*! This is the base output archive for all output archives. If you create
Expand Down
37 changes: 33 additions & 4 deletions include/cereal/details/polymorphic_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,34 @@
have been registered with CEREAL_REGISTER_ARCHIVE. This must be called
after all archives are registered (usually after the archives themselves
have been included). */
#ifdef CEREAL_HAS_CPP17
#define CEREAL_BIND_TO_ARCHIVES(...) \
namespace cereal { \
namespace detail { \
template<> \
struct init_binding<__VA_ARGS__> { \
static bind_to_archives<__VA_ARGS__> const & b; \
static inline bind_to_archives<__VA_ARGS__> const & b= \
::cereal::detail::StaticObject< \
bind_to_archives<__VA_ARGS__> \
>::getInstance().bind(); \
CEREAL_BIND_TO_ARCHIVES_UNUSED_FUNCTION \
}; \
}} /* end namespaces */
#else
#define CEREAL_BIND_TO_ARCHIVES(...) \
namespace cereal { \
namespace detail { \
template<> \
struct init_binding<__VA_ARGS__> { \
static bind_to_archives<__VA_ARGS__> const& b; \
CEREAL_BIND_TO_ARCHIVES_UNUSED_FUNCTION \
}; \
bind_to_archives<__VA_ARGS__> const & init_binding<__VA_ARGS__>::b = \
::cereal::detail::StaticObject< \
bind_to_archives<__VA_ARGS__> \
>::getInstance().bind(); \
}} /* end namespaces */
#endif

namespace cereal
{
Expand Down Expand Up @@ -654,7 +669,7 @@ namespace cereal

auto ptr = PolymorphicCasters::template downcast<T>( dptr, baseInfo );

#if defined(_MSC_VER) && !defined(__clang__)
#if defined(_MSC_VER) && _MSC_VER < 1916 && !defined(__clang__)
savePolymorphicSharedPtr( ar, ptr, ::cereal::traits::has_shared_from_this<T>::type() ); // MSVC doesn't like typename here
#else // not _MSC_VER
savePolymorphicSharedPtr( ar, ptr, typename ::cereal::traits::has_shared_from_this<T>::type() );
Expand All @@ -680,9 +695,23 @@ namespace cereal
//! of instantiate_polymorphic_binding
struct adl_tag {};

//! Tag for init_binding, bind_to_archives and instantiate_polymorphic_binding. Due to the use of anonymous
//! namespace it becomes a different type in each translation unit.
//! Tag for init_binding, bind_to_archives and instantiate_polymorphic_binding.
//! For C++14 and below, we must instantiate a unique StaticObject per TU that is
//! otherwise identical -- otherwise we get multiple definition problems (ODR violations).
//! To achieve this, put a tag in an anonymous namespace and use it as a template argument.
//!
//! For C++17, we can use static inline global variables to unify these definitions across
//! all TUs in the same shared object (DLL). The tag is therefore not necessary.
//! For convenience, keep it to not complicate other code, but don't put it in
//! an anonymous namespace. Now the template instantiations will correspond
//! to the same type, and since they are marked inline with C++17, they will be merged
//! across all TUs.
#ifdef CEREAL_HAS_CPP17
struct polymorphic_binding_tag {};
#else
namespace { struct polymorphic_binding_tag {}; }
#endif


//! Causes the static object bindings between an archive type and a serializable type T
template <class Archive, class T>
Expand Down
4 changes: 3 additions & 1 deletion include/cereal/macros.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@

// ######################################################################
//! Checks if C++17 is available
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
//! NOTE: clang v5 has a bug with inline variables, so disable C++17 on that compiler
#if (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) \
&& (!defined(__clang__) || __clang_major__ > 5)
#define CEREAL_HAS_CPP17
#endif

Expand Down
2 changes: 1 addition & 1 deletion include/cereal/types/polymorphic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
#include "cereal/details/traits.hpp"
#include "cereal/details/polymorphic_impl.hpp"

#ifdef _MSC_VER
#if defined(_MSC_VER) && _MSC_VER < 1916
#define CEREAL_STATIC_CONSTEXPR static
#else
#define CEREAL_STATIC_CONSTEXPR static constexpr
Expand Down

0 comments on commit 06696d0

Please sign in to comment.