LCOV - code coverage report
Current view: top level - cereal/details - helpers.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 23 26 88.5 %
Date: 2022-01-16 21:05:07 Functions: 1183 1235 95.8 %

          Line data    Source code
       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 the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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             :   {
      50           0 :     explicit Exception( const std::string & what_ ) : std::runtime_error(what_) {}
      51         400 :     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 */
     165     4450698 :       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
     179     1480734 :   make_nvp( const char *, T && value )
     180             :   {
     181     1480734 :     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
     192     4446698 :   make_nvp( const char * name, T && value)
     193             :   {
     194     4446698 :     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             : 
     219      287486 :     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 */
     257        8000 :       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       15160 :         OutputArchiveBase() = default;
     273             :         OutputArchiveBase( OutputArchiveBase && ) CEREAL_NOEXCEPT {}
     274             :         OutputArchiveBase & operator=( OutputArchiveBase && ) CEREAL_NOEXCEPT { return *this; }
     275       15160 :         virtual ~OutputArchiveBase() CEREAL_NOEXCEPT = default;
     276             : 
     277             :       private:
     278           0 :         virtual void rtti() {}
     279             :     };
     280             : 
     281             :     class InputArchiveBase
     282             :     {
     283             :       public:
     284       15560 :         InputArchiveBase() = default;
     285             :         InputArchiveBase( InputArchiveBase && ) CEREAL_NOEXCEPT {}
     286             :         InputArchiveBase & operator=( InputArchiveBase && ) CEREAL_NOEXCEPT { return *this; }
     287       15560 :         virtual ~InputArchiveBase() CEREAL_NOEXCEPT = default;
     288             : 
     289             :       private:
     290           0 :         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:
     324      367086 :       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 */
     365     2540352 :     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
     374     2540352 :     void CEREAL_SERIALIZE_FUNCTION_NAME(Archive & archive)
     375             :     {
     376     2540352 :       archive( make_nvp<Archive>("key",   key),
     377     2540352 :                make_nvp<Archive>("value", value) );
     378     2540352 :     }
     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
     385     2540352 :   MapItem<KeyType, ValueType> make_map_item(KeyType && key, ValueType && value)
     386             :   {
     387     2540352 :     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             : 
     413        7600 :       std::uint32_t find( std::size_t hash, std::uint32_t version )
     414             :       {
     415        7600 :         const auto result = mapping.emplace( hash, version );
     416        7600 :         return result.first->second;
     417             :       }
     418             :     }; // struct Versions
     419             :   } // namespace detail
     420             : } // namespace cereal
     421             : 
     422             : #endif // CEREAL_DETAILS_HELPERS_HPP_

Generated by: LCOV version 1.14