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_
|