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