1 /*! \file helpers.hpp 2 \brief Internal helper functionality 3 \ingroup Internal */ 4 /* 5 Copyright (c) 2014, Randolph Voorhies, Shane Grant 6 All rights reserved. 7 8 Redistribution and use in source and binary forms, with or without 9 modification, are permitted provided that the following conditions are met: 10 * Redistributions of source code must retain the above copyright 11 notice, this list of conditions and the following disclaimer. 12 * Redistributions in binary form must reproduce the above copyright 13 notice, this list of conditions and the following disclaimer in the 14 documentation and/or other materials provided with the distribution. 15 * Neither the name of cereal nor the 16 names of its contributors may be used to endorse or promote products 17 derived from this software without specific prior written permission. 18 19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY 23 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 #ifndef CEREAL_DETAILS_HELPERS_HPP_ 31 #define CEREAL_DETAILS_HELPERS_HPP_ 32 33 #include <type_traits> 34 #include <cstdint> 35 #include <utility> 36 #include <memory> 37 #include <unordered_map> 38 #include <stdexcept> 39 40 #include "cereal/macros.hpp" 41 #include "cereal/details/static_object.hpp" 42 43 namespace cereal 44 { 45 // ###################################################################### 46 //! An exception class thrown when things go wrong at runtime 47 /*! @ingroup Utility */ 48 struct Exception : public std::runtime_error 49 { Exceptioncereal::Exception50 explicit Exception( const std::string & what_ ) : std::runtime_error(what_) {} Exceptioncereal::Exception51 explicit Exception( const char * what_ ) : std::runtime_error(what_) {} 52 }; 53 54 // ###################################################################### 55 //! The size type used by cereal 56 /*! To ensure compatability between 32, 64, etc bit machines, we need to use 57 a fixed size type instead of size_t, which may vary from machine to 58 machine. 59 60 The default value for CEREAL_SIZE_TYPE is specified in cereal/macros.hpp */ 61 using size_type = CEREAL_SIZE_TYPE; 62 63 // forward decls 64 class BinaryOutputArchive; 65 class BinaryInputArchive; 66 67 // ###################################################################### 68 namespace detail 69 { 70 struct NameValuePairCore {}; //!< Traits struct for NVPs 71 struct DeferredDataCore {}; //!< Traits struct for DeferredData 72 } 73 74 // ###################################################################### 75 //! For holding name value pairs 76 /*! This pairs a name (some string) with some value such that an archive 77 can potentially take advantage of the pairing. 78 79 In serialization functions, NameValuePairs are usually created like so: 80 @code{.cpp} 81 struct MyStruct 82 { 83 int a, b, c, d, e; 84 85 template<class Archive> 86 void serialize(Archive & archive) 87 { 88 archive( CEREAL_NVP(a), 89 CEREAL_NVP(b), 90 CEREAL_NVP(c), 91 CEREAL_NVP(d), 92 CEREAL_NVP(e) ); 93 } 94 }; 95 @endcode 96 97 Alternatively, you can give you data members custom names like so: 98 @code{.cpp} 99 struct MyStruct 100 { 101 int a, b, my_embarrassing_variable_name, d, e; 102 103 template<class Archive> 104 void serialize(Archive & archive) 105 { 106 archive( CEREAL_NVP(a), 107 CEREAL_NVP(b), 108 cereal::make_nvp("var", my_embarrassing_variable_name) ); 109 CEREAL_NVP(d), 110 CEREAL_NVP(e) ); 111 } 112 }; 113 @endcode 114 115 There is a slight amount of overhead to creating NameValuePairs, so there 116 is a third method which will elide the names when they are not used by 117 the Archive: 118 119 @code{.cpp} 120 struct MyStruct 121 { 122 int a, b; 123 124 template<class Archive> 125 void serialize(Archive & archive) 126 { 127 archive( cereal::make_nvp<Archive>(a), 128 cereal::make_nvp<Archive>(b) ); 129 } 130 }; 131 @endcode 132 133 This third method is generally only used when providing generic type 134 support. Users writing their own serialize functions will normally 135 explicitly control whether they want to use NVPs or not. 136 137 @internal */ 138 template <class T> 139 class NameValuePair : detail::NameValuePairCore 140 { 141 private: 142 // If we get passed an array, keep the type as is, otherwise store 143 // a reference if we were passed an l value reference, else copy the value 144 using Type = typename std::conditional<std::is_array<typename std::remove_reference<T>::type>::value, 145 typename std::remove_cv<T>::type, 146 typename std::conditional<std::is_lvalue_reference<T>::value, 147 T, 148 typename std::decay<T>::type>::type>::type; 149 150 // prevent nested nvps 151 static_assert( !std::is_base_of<detail::NameValuePairCore, T>::value, 152 "Cannot pair a name to a NameValuePair" ); 153 154 NameValuePair & operator=( NameValuePair const & ) = delete; 155 156 public: 157 //! Constructs a new NameValuePair 158 /*! @param n The name of the pair 159 @param v The value to pair. Ideally this should be an l-value reference so that 160 the value can be both loaded and saved to. If you pass an r-value reference, 161 the NameValuePair will store a copy of it instead of a reference. Thus you should 162 only pass r-values in cases where this makes sense, such as the result of some 163 size() call. 164 @internal */ NameValuePair(char const * n,T && v)165 NameValuePair( char const * n, T && v ) : name(n), value(std::forward<T>(v)) {} 166 167 char const * name; 168 Type value; 169 }; 170 171 //! A specialization of make_nvp<> that simply forwards the value for binary archives 172 /*! @relates NameValuePair 173 @internal */ 174 template<class Archive, class T> inline 175 typename 176 std::enable_if<std::is_same<Archive, ::cereal::BinaryInputArchive>::value || 177 std::is_same<Archive, ::cereal::BinaryOutputArchive>::value, 178 T && >::type make_nvp(const char *,T && value)179 make_nvp( const char *, T && value ) 180 { 181 return std::forward<T>(value); 182 } 183 184 //! A specialization of make_nvp<> that actually creates an nvp for non-binary archives 185 /*! @relates NameValuePair 186 @internal */ 187 template<class Archive, class T> inline 188 typename 189 std::enable_if<!std::is_same<Archive, ::cereal::BinaryInputArchive>::value && 190 !std::is_same<Archive, ::cereal::BinaryOutputArchive>::value, 191 NameValuePair<T> >::type make_nvp(const char * name,T && value)192 make_nvp( const char * name, T && value) 193 { 194 return {name, std::forward<T>(value)}; 195 } 196 197 //! Convenience for creating a templated NVP 198 /*! For use in internal generic typing functions which have an 199 Archive type declared 200 @internal */ 201 #define CEREAL_NVP_(name, value) ::cereal::make_nvp<Archive>(name, value) 202 203 // ###################################################################### 204 //! A wrapper around data that can be serialized in a binary fashion 205 /*! This class is used to demarcate data that can safely be serialized 206 as a binary chunk of data. Individual archives can then choose how 207 best represent this during serialization. 208 209 @internal */ 210 template <class T> 211 struct BinaryData 212 { 213 //! Internally store the pointer as a void *, keeping const if created with 214 //! a const pointer 215 using PT = typename std::conditional<std::is_const<typename std::remove_pointer<typename std::remove_reference<T>::type>::type>::value, 216 const void *, 217 void *>::type; 218 BinaryDatacereal::BinaryData219 BinaryData( T && d, uint64_t s ) : data(std::forward<T>(d)), size(s) {} 220 221 PT data; //!< pointer to beginning of data 222 uint64_t size; //!< size in bytes 223 }; 224 225 // ###################################################################### 226 //! A wrapper around data that should be serialized after all non-deferred data 227 /*! This class is used to demarcate data that can only be safely serialized after 228 any data not wrapped in this class. 229 230 @internal */ 231 template <class T> 232 class DeferredData : detail::DeferredDataCore 233 { 234 private: 235 // If we get passed an array, keep the type as is, otherwise store 236 // a reference if we were passed an l value reference, else copy the value 237 using Type = typename std::conditional<std::is_array<typename std::remove_reference<T>::type>::value, 238 typename std::remove_cv<T>::type, 239 typename std::conditional<std::is_lvalue_reference<T>::value, 240 T, 241 typename std::decay<T>::type>::type>::type; 242 243 // prevent nested nvps 244 static_assert( !std::is_base_of<detail::DeferredDataCore, T>::value, 245 "Cannot defer DeferredData" ); 246 247 DeferredData & operator=( DeferredData const & ) = delete; 248 249 public: 250 //! Constructs a new NameValuePair 251 /*! @param v The value to defer. Ideally this should be an l-value reference so that 252 the value can be both loaded and saved to. If you pass an r-value reference, 253 the DeferredData will store a copy of it instead of a reference. Thus you should 254 only pass r-values in cases where this makes sense, such as the result of some 255 size() call. 256 @internal */ DeferredData(T && v)257 DeferredData( T && v ) : value(std::forward<T>(v)) {} 258 259 Type value; 260 }; 261 262 // ###################################################################### 263 namespace detail 264 { 265 // base classes for type checking 266 /* The rtti virtual function only exists to enable an archive to 267 be used in a polymorphic fashion, if necessary. See the 268 archive adapters for an example of this */ 269 class OutputArchiveBase 270 { 271 public: 272 OutputArchiveBase() = default; OutputArchiveBase(OutputArchiveBase &&)273 OutputArchiveBase( OutputArchiveBase && ) CEREAL_NOEXCEPT {} operator =(OutputArchiveBase &&)274 OutputArchiveBase & operator=( OutputArchiveBase && ) CEREAL_NOEXCEPT { return *this; } 275 virtual ~OutputArchiveBase() CEREAL_NOEXCEPT = default; 276 277 private: rtti()278 virtual void rtti() {} 279 }; 280 281 class InputArchiveBase 282 { 283 public: 284 InputArchiveBase() = default; InputArchiveBase(InputArchiveBase &&)285 InputArchiveBase( InputArchiveBase && ) CEREAL_NOEXCEPT {} operator =(InputArchiveBase &&)286 InputArchiveBase & operator=( InputArchiveBase && ) CEREAL_NOEXCEPT { return *this; } 287 virtual ~InputArchiveBase() CEREAL_NOEXCEPT = default; 288 289 private: rtti()290 virtual void rtti() {} 291 }; 292 293 // forward decls for polymorphic support 294 template <class Archive, class T> struct polymorphic_serialization_support; 295 struct adl_tag; 296 297 // used during saving pointers 298 static const uint32_t msb_32bit = 0x80000000; 299 static const int32_t msb2_32bit = 0x40000000; 300 } 301 302 // ###################################################################### 303 //! A wrapper around size metadata 304 /*! This class provides a way for archives to have more flexibility over how 305 they choose to serialize size metadata for containers. For some archive 306 types, the size may be implicitly encoded in the output (e.g. JSON) and 307 not need an explicit entry. Specializing serialize or load/save for 308 your archive and SizeTags allows you to choose what happens. 309 310 @internal */ 311 template <class T> 312 class SizeTag 313 { 314 private: 315 // Store a reference if passed an lvalue reference, otherwise 316 // make a copy of the data 317 using Type = typename std::conditional<std::is_lvalue_reference<T>::value, 318 T, 319 typename std::decay<T>::type>::type; 320 321 SizeTag & operator=( SizeTag const & ) = delete; 322 323 public: SizeTag(T && sz)324 SizeTag( T && sz ) : size(std::forward<T>(sz)) {} 325 326 Type size; 327 }; 328 329 // ###################################################################### 330 //! A wrapper around a key and value for serializing data into maps. 331 /*! This class just provides a grouping of keys and values into a struct for 332 human readable archives. For example, XML archives will use this wrapper 333 to write maps like so: 334 335 @code{.xml} 336 <mymap> 337 <item0> 338 <key>MyFirstKey</key> 339 <value>MyFirstValue</value> 340 </item0> 341 <item1> 342 <key>MySecondKey</key> 343 <value>MySecondValue</value> 344 </item1> 345 </mymap> 346 @endcode 347 348 \sa make_map_item 349 @internal */ 350 template <class Key, class Value> 351 struct MapItem 352 { 353 using KeyType = typename std::conditional< 354 std::is_lvalue_reference<Key>::value, 355 Key, 356 typename std::decay<Key>::type>::type; 357 358 using ValueType = typename std::conditional< 359 std::is_lvalue_reference<Value>::value, 360 Value, 361 typename std::decay<Value>::type>::type; 362 363 //! Construct a MapItem from a key and a value 364 /*! @internal */ MapItemcereal::MapItem365 MapItem( Key && key_, Value && value_ ) : key(std::forward<Key>(key_)), value(std::forward<Value>(value_)) {} 366 367 MapItem & operator=( MapItem const & ) = delete; 368 369 KeyType key; 370 ValueType value; 371 372 //! Serialize the MapItem with the NVPs "key" and "value" 373 template <class Archive> inline CEREAL_SERIALIZE_FUNCTION_NAMEcereal::MapItem374 void CEREAL_SERIALIZE_FUNCTION_NAME(Archive & archive) 375 { 376 archive( make_nvp<Archive>("key", key), 377 make_nvp<Archive>("value", value) ); 378 } 379 }; 380 381 //! Create a MapItem so that human readable archives will group keys and values together 382 /*! @internal 383 @relates MapItem */ 384 template <class KeyType, class ValueType> inline make_map_item(KeyType && key,ValueType && value)385 MapItem<KeyType, ValueType> make_map_item(KeyType && key, ValueType && value) 386 { 387 return {std::forward<KeyType>(key), std::forward<ValueType>(value)}; 388 } 389 390 namespace detail 391 { 392 //! Tag for Version, which due to its anonymous namespace, becomes a different 393 //! type in each translation unit 394 /*! This allows CEREAL_CLASS_VERSION to be safely called in a header file */ 395 namespace{ struct version_binding_tag {}; } 396 397 // ###################################################################### 398 //! Version information class 399 /*! This is the base case for classes that have not been explicitly 400 registered */ 401 template <class T, class BindingTag = version_binding_tag> struct Version 402 { 403 static const std::uint32_t version = 0; 404 // we don't need to explicitly register these types since they 405 // always get a version number of 0 406 }; 407 408 //! Holds all registered version information 409 struct Versions 410 { 411 std::unordered_map<std::size_t, std::uint32_t> mapping; 412 findcereal::detail::Versions413 std::uint32_t find( std::size_t hash, std::uint32_t version ) 414 { 415 const auto result = mapping.emplace( hash, version ); 416 return result.first->second; 417 } 418 }; // struct Versions 419 } // namespace detail 420 } // namespace cereal 421 422 #endif // CEREAL_DETAILS_HELPERS_HPP_ 423