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 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 : {
50 0 : explicit Exception( const std::string & what_ ) : std::runtime_error(what_) {}
51 800 : 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 8894952 : 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 2960868 : make_nvp( const char *, T && value )
180 : {
181 2960868 : return std::forward<T>(value);
182 : }
183 123564 :
184 : //! A specialization of make_nvp<> that actually creates an nvp for non-binary archives
185 123564 : /*! @relates NameValuePair
186 : @internal */
187 127951 : template<class Archive, class T> inline
188 : typename
189 127951 : std::enable_if<!std::is_same<Archive, ::cereal::BinaryInputArchive>::value &&
190 : !std::is_same<Archive, ::cereal::BinaryOutputArchive>::value,
191 112206 : NameValuePair<T> >::type
192 1600 : make_nvp( const char * name, T && value)
193 112206 : {
194 1600 : return {name, std::forward<T>(value)};
195 167322 : }
196 200 :
197 167322 : //! Convenience for creating a templated NVP
198 200 : /*! For use in internal generic typing functions which have an
199 226539 : Archive type declared
200 7400 : @internal */
201 226539 : #define CEREAL_NVP_(name, value) ::cereal::make_nvp<Archive>(name, value)
202 7400 :
203 145545 : // ######################################################################
204 1400 : //! A wrapper around data that can be serialized in a binary fashion
205 145545 : /*! This class is used to demarcate data that can safely be serialized
206 1400 : as a binary chunk of data. Individual archives can then choose how
207 203228 : best represent this during serialization.
208 12200 :
209 203228 : @internal */
210 12200 : template <class T>
211 126807 : struct BinaryData
212 12600 : {
213 126807 : //! Internally store the pointer as a void *, keeping const if created with
214 12600 : //! a const pointer
215 119164 : using PT = typename std::conditional<std::is_const<typename std::remove_pointer<typename std::remove_reference<T>::type>::type>::value,
216 259857 : const void *,
217 119164 : void *>::type;
218 259857 :
219 123523 : BinaryData( T && d, uint64_t s ) : data(std::forward<T>(d)), size(s) {}
220 25605 :
221 116323 : PT data; //!< pointer to beginning of data
222 25605 : uint64_t size; //!< size in bytes
223 117565 : };
224 61900 :
225 117565 : // ######################################################################
226 61900 : //! A wrapper around data that should be serialized after all non-deferred data
227 103616 : /*! This class is used to demarcate data that can only be safely serialized after
228 24754 : any data not wrapped in this class.
229 103616 :
230 24754 : @internal */
231 123370 : template <class T>
232 60247 : class DeferredData : detail::DeferredDataCore
233 123370 : {
234 60247 : private:
235 121586 : // If we get passed an array, keep the type as is, otherwise store
236 23847 : // a reference if we were passed an l value reference, else copy the value
237 121586 : using Type = typename std::conditional<std::is_array<typename std::remove_reference<T>::type>::value,
238 23847 : typename std::remove_cv<T>::type,
239 321982 : typename std::conditional<std::is_lvalue_reference<T>::value,
240 23830 : T,
241 321982 : typename std::decay<T>::type>::type>::type;
242 23830 :
243 121400 : // prevent nested nvps
244 24777 : static_assert( !std::is_base_of<detail::DeferredDataCore, T>::value,
245 121400 : "Cannot defer DeferredData" );
246 24777 :
247 82200 : DeferredData & operator=( DeferredData const & ) = delete;
248 26516 :
249 82200 : public:
250 26516 : //! Constructs a new NameValuePair
251 99762 : /*! @param v The value to defer. Ideally this should be an l-value reference so that
252 4718 : the value can be both loaded and saved to. If you pass an r-value reference,
253 98962 : the DeferredData will store a copy of it instead of a reference. Thus you should
254 4718 : only pass r-values in cases where this makes sense, such as the result of some
255 60802 : size() call.
256 2404810 : @internal */
257 60802 : DeferredData( T && v ) : value(std::forward<T>(v)) {}
258 2404810 :
259 61765 : Type value;
260 44501 : };
261 61765 :
262 44501 : // ######################################################################
263 18967 : namespace detail
264 1223308 : {
265 18967 : // base classes for type checking
266 1223308 : /* The rtti virtual function only exists to enable an archive to
267 26548 : be used in a polymorphic fashion, if necessary. See the
268 75497 : archive adapters for an example of this */
269 20804 : class OutputArchiveBase
270 81241 : {
271 21000 : public:
272 2499111 : OutputArchiveBase() = default;
273 21000 : OutputArchiveBase( OutputArchiveBase && ) CEREAL_NOEXCEPT {}
274 2493367 : OutputArchiveBase & operator=( OutputArchiveBase && ) CEREAL_NOEXCEPT { return *this; }
275 26744 : virtual ~OutputArchiveBase() CEREAL_NOEXCEPT = default;
276 104711 :
277 21000 : private:
278 104711 : virtual void rtti() {}
279 26544 : };
280 166535 :
281 20800 : class InputArchiveBase
282 172279 : {
283 21000 : public:
284 190830 : InputArchiveBase() = default;
285 21000 : InputArchiveBase( InputArchiveBase && ) CEREAL_NOEXCEPT {}
286 185086 : InputArchiveBase & operator=( InputArchiveBase && ) CEREAL_NOEXCEPT { return *this; }
287 26744 : virtual ~InputArchiveBase() CEREAL_NOEXCEPT = default;
288 124039 :
289 21000 : private:
290 124039 : virtual void rtti() {}
291 20800 : };
292 123686 :
293 20800 : // forward decls for polymorphic support
294 123686 : template <class Archive, class T> struct polymorphic_serialization_support;
295 21600 : struct adl_tag;
296 105806 :
297 21600 : // used during saving pointers
298 105806 : static const uint32_t msb_32bit = 0x80000000;
299 22600 : static const int32_t msb2_32bit = 0x40000000;
300 103406 : }
301 21600 :
302 104406 : // ######################################################################
303 41600 : //! A wrapper around size metadata
304 101187 : /*! This class provides a way for archives to have more flexibility over how
305 41600 : they choose to serialize size metadata for containers. For some archive
306 100187 : types, the size may be implicitly encoded in the output (e.g. JSON) and
307 23400 : not need an explicit entry. Specializing serialize or load/save for
308 100584 : your archive and SizeTags allows you to choose what happens.
309 22400 :
310 100584 : @internal */
311 1200 : template <class T>
312 106017 : class SizeTag
313 200 : {
314 107017 : private:
315 81484 : // Store a reference if passed an lvalue reference, otherwise
316 249603 : // make a copy of the data
317 200 : using Type = typename std::conditional<std::is_lvalue_reference<T>::value,
318 248603 : T,
319 39600 : typename std::decay<T>::type>::type;
320 2486543 :
321 200 : SizeTag & operator=( SizeTag const & ) = delete;
322 2486543 :
323 400 : public:
324 157116 : SizeTag( T && sz ) : size(std::forward<T>(sz)) {}
325 400 :
326 118716 : Type size;
327 400 : };
328 144989 :
329 400 : // ######################################################################
330 144989 : //! A wrapper around a key and value for serializing data into maps.
331 612 : /*! This class just provides a grouping of keys and values into a struct for
332 121803 : human readable archives. For example, XML archives will use this wrapper
333 200 : to write maps like so:
334 122203 :
335 200 : @code{.xml}
336 124603 : <mymap>
337 200 : <item0>
338 124203 : <key>MyFirstKey</key>
339 600 : <value>MyFirstValue</value>
340 120183 : </item0>
341 200 : <item1>
342 120183 : <key>MySecondKey</key>
343 1200 : <value>MySecondValue</value>
344 116335 : </item1>
345 800 : </mymap>
346 116735 : @endcode
347 400 :
348 207754 : \sa make_map_item
349 400 : @internal */
350 199354 : template <class Key, class Value>
351 1600 : struct MapItem
352 104002 : {
353 8400 : using KeyType = typename std::conditional<
354 104002 : std::is_lvalue_reference<Key>::value,
355 2000 : Key,
356 104008 : typename std::decay<Key>::type>::type;
357 2000 :
358 103208 : using ValueType = typename std::conditional<
359 200 : std::is_lvalue_reference<Value>::value,
360 121000 : Value,
361 200 : typename std::decay<Value>::type>::type;
362 121000 :
363 1404 : //! Construct a MapItem from a key and a value
364 261190 : /*! @internal */
365 200 : MapItem( Key && key_, Value && value_ ) : key(std::forward<Key>(key_)), value(std::forward<Value>(value_)) {}
366 262390 :
367 200 : MapItem & operator=( MapItem const & ) = delete;
368 122004 :
369 200 : KeyType key;
370 120804 : ValueType value;
371 1800 :
372 201004 : //! Serialize the MapItem with the NVPs "key" and "value"
373 200 : template <class Archive> inline
374 201404 : void CEREAL_SERIALIZE_FUNCTION_NAME(Archive & archive)
375 1600 : {
376 141606 : archive( make_nvp<Archive>("key", key),
377 0 : make_nvp<Archive>("value", value) );
378 142806 : }
379 408 : };
380 199207 :
381 0 : //! Create a MapItem so that human readable archives will group keys and values together
382 197615 : /*! @internal
383 2000 : @relates MapItem */
384 170160 : template <class KeyType, class ValueType> inline
385 0 : MapItem<KeyType, ValueType> make_map_item(KeyType && key, ValueType && value)
386 170556 : {
387 4 : return {std::forward<KeyType>(key), std::forward<ValueType>(value)};
388 117554 : }
389 0 :
390 117154 : namespace detail
391 408 : {
392 103787 : //! Tag for Version, which due to its anonymous namespace, becomes a different
393 : //! type in each translation unit
394 103795 : /*! This allows CEREAL_CLASS_VERSION to be safely called in a header file */
395 400 : namespace{ struct version_binding_tag {}; }
396 102992 :
397 0 : // ######################################################################
398 103388 : //! Version information class
399 4 : /*! This is the base case for classes that have not been explicitly
400 142175 : registered */
401 0 : template <class T, class BindingTag = version_binding_tag> struct Version
402 141775 : {
403 400 : static const std::uint32_t version = 0;
404 201000 : // we don't need to explicitly register these types since they
405 : // always get a version number of 0
406 201000 : };
407 400 :
408 121000 : //! Holds all registered version information
409 : struct Versions
410 121400 : {
411 8 : std::unordered_map<std::size_t, std::uint32_t> mapping;
412 199003 :
413 0 : std::uint32_t find( std::size_t hash, std::uint32_t version )
414 198603 : {
415 44404 : const auto result = mapping.emplace( hash, version );
416 117411 : return result.first->second;
417 : }
418 117407 : }; // struct Versions
419 4 : } // namespace detail
420 146282 : } // namespace cereal
421 :
422 102602 : #endif // CEREAL_DETAILS_HELPERS_HPP_
|