1 #pragma once
2 #ifndef CATA_SRC_CATA_IO_H
3 #define CATA_SRC_CATA_IO_H
4 
5 #include <functional>
6 #include <string>
7 #include <type_traits>
8 
9 #include "json.h"
10 
11 /**
12  * @name Serialization and deserialization
13  *
14  * Basic classes are the input and output archives. Each of them comes in two flavors: one stores
15  * data in a JSON array and the other stores them in a JSON object.
16  *
17  * The input archive is used to deserialize (ie.e read from the archive, write to a C++ variable),
18  * the output archive does the inverse.
19  *
20  * Input and output archives (that refer to the same JSON type, i.e. @ref JsonArrayInputArchive
21  * and @ref JsonArrayOutputArchive) have (nearly) the same interface (mostly the `io` function),
22  * they only differ in what the functions do. This is an important design decision, it allows
23  * the archive type to be a template parameter. The `io` functions in both archive classes should
24  * therefore have a compatible signature.
25  *
26  * Archive classes also have a `is_input` member type, which is either @ref std::true_type or
27  * if(the class is an input archive) or @ref std::false_type (it's an output archive). This can be
28  * used to do additional things only after loading the data, but not after storing it.
29  *
30  * Usage: implement a tempted function `io` in the class that is to be serialized and add a typedef
31  *
32  *     class MySerializeableType {
33  *         ...
34  *         template<typename Archive>
35  *         void item::io( Archive& archive ) {
36  *             archive.io( "member1", member1, default_value_of_member1 );
37  *             archive.io( "member2", member2, default_value_of_member2 );
38  *             ...
39  *         }
40  *         using archive_type_tag = io::object_archive_tag;
41  *         ...
42  *     };
43  *
44  * The tag types are defined in io_tags.h so that this header need not be
45  * included to use them.
46  *
47  * The function must be declared with the archive type as template parameter, but it can be
48  * implemented outside of the header (e.g. in savegame_json.cpp). It will be instantiated with
49  * JsonArrayInputArchive and JsonArrayOutputArchive, or with JsonObjectInputArchive and
50  * JsonObjectOutputArchive as archive type, depending on the type of the archive_type_tag.
51  *
52  * Note that the array based archives have a much simpler interface, they only support
53  * reading/writing sequentially.
54  *
55  * The `io` function works for input and output archives. One can call it like this:
56  *
57  *     item& tmp = ...
58  *     JsonOutputArchive out( ... );
59  *     tmp.io( out );
60  *     JsonInputArchive in( ... );
61  *     tmp.io( in );
62  *
63  * The archive classes use the Json classes (see json.h) to write and read data. This works fine
64  * for most built-in types (including string and certain container types). Specific classes, like
65  * @ref item or @ref monster, need their own logic. They used to inherit from @ref JsonDeserializer
66  * and @ref JsonSerializer, which would grant them functions to serialize/deserialize. The Json
67  * classes will automatically use those if available, but they prefer the `io` function.
68  *
69  * Classes that only use the (templated) `io` function (and which do not inherit from
70  * JsonDeserializer and JsonSerializer), must announce that. This allows the Archive classes to
71  * avoid calling Json functions, which would not work.
72  *
73  * If a class only uses the `io` function defined like this:
74  *
75  *     template<typename Archive> void io( Archive& );
76  *
77  * It should also have a type tag that indicates 1) the`io` function can be used and 2) whether
78  * the data of the class is stored in a JSON array (@ref io::array_archive_tag) or in a
79  * JSON object (@ref io::object_archive_tag).
80  *
81  *     using archive_type_tag = io::object_archive_tag;
82  *
83  * Note that the *archive_tag structs only need to be forward declared to work in the using
84  * statement. One does not need to include "io.h" to allow that statement.
85  */
86 namespace io
87 {
88 
89 /**
90  * Tag that indicates the default value for io should be the default value of the type
91  * itself, i.e. the result of default initialization: `T()`
92  * The type must support a comparison, so serializing can check whether the value is equal to the
93  * default value.
94  *
95  * An instance of this tag can be used as default value parameter in the io functions.
96  */
97 struct default_tag { };
98 /**
99  * Tag that indicates the value of the io is some kind of container and its default value is to
100  * be empty. This does not require it to have a comparison function (e.g. suitable for @ref item).
101  * It requires the type to have `bool empty()` and `void clear()` functions (i.e. works for maps).
102  *
103  * An instance of this tag can be used as default value parameter in the io functions.
104  */
105 struct empty_default_tag { };
106 
107 /**
108  * Tag that indicates the value is required and must exists in the JSON data.
109  */
110 struct required_tag { };
111 
112 /**
113  * The namespace contains classes that do write/read to/from JSON via either the Json classes in
114  * "json.h" or via the `io` function of the object to be written/read.
115  * Which of those classes is actually used determined via the template parameter and use of SFINAE.
116  * Usage:
117  *
118  *     io::detail::has_archive_tag<SomeType>::write( stream, instance_of_SomeType );
119  */
120 namespace detail
121 {
122 
123 template<class T, class R = void>
124 struct enable_if_type {
125     using type = R;
126 };
127 
128 /**
129  * Implementation for classes that don't have an archive_type_tag defined. They use the
130  * normal JsonSerializer / JsonDeserializer interface, which is handled directly by the Json
131  * classes. Therefore the functions here simply forward to those.
132  */
133 template<class T, class E = void>
134 struct has_archive_tag : std::false_type {
writehas_archive_tag135     static void write( JsonOut &stream, const T &value ) {
136         stream.write( value );
137     }
readhas_archive_tag138     static bool read( const JsonObject &obj, const std::string &key, T &value ) {
139         return obj.read( key, value );
140     }
readhas_archive_tag141     static bool read( JsonArray &arr, T &value ) {
142         return arr.read_next( value );
143     }
144 };
145 
146 /**
147  * Implementation for classes that use the `io` function. The function herein create Archive
148  * instances and call the `io` function of the object to be written/read.
149  */
150 template<class T>
151 struct has_archive_tag<T, typename enable_if_type<typename T::archive_type_tag>::type> :
152     std::true_type {
153     using InArchive = typename T::archive_type_tag::InputArchive;
154 
155     static void write( JsonOut &stream, const T &value ) {
156         typename T::archive_type_tag::OutputArchive archive( stream );
157         const_cast<T &>( value ).io( archive );
158     }
159     static bool read( const JsonObject &obj, const std::string &key, T &value ) {
160         if( !obj.has_member( key ) ) {
161             return false;
162         }
163         InArchive archive( obj, key );
164         value.io( archive );
165         return true;
166     }
167     static bool read( JsonArray &arr, T &value ) {
168         if( !arr.has_more() ) {
169             return false;
170         }
171         InArchive archive( arr );
172         value.io( archive );
173         return true;
174     }
175 };
176 
177 } // namespace detail
178 
179 /**
180  * Input archive reading data from a Json object.
181  *
182  * Data can be loaded from this archive using the name of the member as key.
183  */
184 class JsonObjectInputArchive : public JsonObject
185 {
186     public:
187         using is_input = std::true_type;
188 
189         explicit JsonObjectInputArchive( const JsonObject &jo )
190             : JsonObject( jo ) {
191         }
192         /** Create archive from next object in the given Json array. */
193         explicit JsonObjectInputArchive( JsonArray & );
194         /** Create archive from named member object in the given Json object. */
195         JsonObjectInputArchive( const JsonObject &, const std::string &key );
196 
197         /**
198          * @name Deserialization
199          *
200          * The io functions read a value from the archive and store it in the reference parameter.
201          *
202          * @throw JsonError (via the Json classes) if the value in the archive is of an incompatible
203          * type (e.g. reading a string, but the member is a Json object).
204          * @return `false` if the archive did not contain the requested member, otherwise `true`.
205          */
206         /*@{*/
207         /**
208          * If the archive does not have the requested member, the value is not changed at all.
209          */
210         template<typename T>
211         bool io( const std::string &name, T &value ) {
212             return io::detail::has_archive_tag<T>::read( *this, name, value );
213         }
214         /**
215          * If the archive does not have the requested member, the given default value is assigned.
216          * The function still returns false if the default value had been used.
217          */
218         template<typename T>
219         bool io( const std::string &name, T &value, const T &default_value ) {
220             if( io( name, value ) ) {
221                 return true;
222             }
223             value = default_value;
224             return false;
225         }
226         /**
227          * Like the other io function, this uses a default constructed default value:
228          * Roughly equivalent to \code io<T>( name, value, T() ); \endcode
229          */
230         template<typename T>
231         bool io( const std::string &name, T &value, default_tag ) {
232             static const T default_value = T();
233             return io( name, value, default_value );
234         }
235         /**
236          * If the archive does not have the requested member, the function `clear` is called on the
237          * value, this may be used for containers (e.g. std::map).
238          */
239         template<typename T>
240         bool io( const std::string &name, T &value, empty_default_tag ) {
241             if( io( name, value ) ) {
242                 return true;
243             }
244             value.clear();
245             return false;
246         }
247         /**
248          * Special function to load pointers. If the archive does not have the requested member,
249          * `nullptr` is assigned to the pointer. Otherwise the value is loaded (which must be a
250          * `std::string`) and the load function is called.
251          *
252          * Example:
253          *
254          *     class Dummy {
255          *         int *pointer;
256          *         template<typename A> void io( A& ar ) {
257          *             ar.io( "ptr", pointer,
258          *                 [this](const std::string&s) {
259          *                     pointer = new int( atoi( s.c_str() ) );
260          *                 },
261          *                 [](const int &v) {
262          *                     return std::to_string( v );
263          *                 }
264          *             );
265          *         }
266          *     };
267          *
268          * @param name Name of requested member
269          * @param pointer
270          * @param load The function gets the string that was loaded from JSON and should set the pointer
271          * in the object accordingly. One would usually use a lambda for this function.
272          * @param save The inverse of the load function, it converts the pointer to a string which is
273          * later stored in the JSON.
274          * @param required If `true`, an error will be raised if the requested member does not exist
275          * in the JSON data.
276          * JsonOutputArchive, so it can be used when the archive type is a template parameter.
277          */
278         template<typename T>
279         bool io( const std::string &name, T *&pointer,
280                  const std::function<void( const std::string & )> &load,
281                  const std::function<std::string( const T & )> &save, bool required = false ) {
282             // Only used by the matching function in the output archive classes.
283             ( void ) save;
284             std::string ident;
285             if( !io( name, ident ) ) {
286                 if( required ) {
287                     JsonObject::throw_error( std::string( "required member is missing: " ) + name );
288                 }
289                 pointer = nullptr;
290                 return false;
291             }
292             load( ident );
293             return true;
294         }
295         template<typename T>
296         bool io( const std::string &name, T *&pointer,
297                  const std::function<void( const std::string & )> &load,
298                  const std::function<std::string( const T & )> &save, required_tag ) {
299             return io<T>( name, pointer, load, save, true );
300         }
301         /*@}*/
302 };
303 
304 /**
305  * Input archive reading data from a Json array.
306  *
307  * This archive is quite simple, it loads the data sequentially from the Json array. The first
308  * call to @ref io loads the first value, the next call loads the second and so on.
309  *
310  * Reading a value from a specific index is on purpose not supported.
311  */
312 class JsonArrayInputArchive : public JsonArray
313 {
314     public:
315         using is_input = std::true_type;
316 
317         explicit JsonArrayInputArchive( const JsonArray &jo )
318             : JsonArray( jo ) {
319         }
320         /** Create archive from next object in the giexplicit ven Json array. */
321         explicit JsonArrayInputArchive( JsonArray & );
322         /** Create archive from named member object in the given Json object. */
323         JsonArrayInputArchive( const JsonObject &, const std::string &key );
324 
325         template<typename T>
326         bool io( T &value ) {
327             return io::detail::has_archive_tag<T>::read( *this, value );
328         }
329 };
330 
331 inline JsonArrayInputArchive::JsonArrayInputArchive( JsonArray &arr )
332     : JsonArray( arr.next_array() ) { }
333 inline JsonArrayInputArchive::JsonArrayInputArchive( const JsonObject &obj, const std::string &key )
334     : JsonArray( obj.get_array( key ) ) { }
335 
336 inline JsonObjectInputArchive::JsonObjectInputArchive( JsonArray &arr )
337     : JsonObject( arr.next_object() ) { }
338 inline JsonObjectInputArchive::JsonObjectInputArchive( const JsonObject &obj,
339         const std::string &key )
340     : JsonObject( obj.get_object( key ) ) { }
341 
342 /**
343  * Output archive matching the input archive @ref JsonObjectInputArchive.
344  *
345  * It has the same function as the input archive, they do the exact inverse.
346  *
347  * The class creates a valid JSON value in the output stream (that means it adds the initial
348  * '{' and '}' to the stream).
349  */
350 class JsonObjectOutputArchive
351 {
352     public:
353         using is_input = std::false_type;
354 
355         JsonOut &stream;
356 
357         explicit JsonObjectOutputArchive( JsonOut &stream )
358             : stream( stream ) {
359             stream.start_object();
360         }
361         ~JsonObjectOutputArchive() {
362             stream.end_object();
363         }
364 
365         /**
366          * @name Serialization
367          *
368          * The io functions store a value (given as parameter) in the archive.
369          *
370          * @throw JsonError (via the Json classes) on any kind of error.
371          *
372          * @return All functions return `false`. Their signature should be compatible with the
373          * functions in @ref JsonObjectInputArchive, so they can be used when the archive type is a
374          * template parameter.
375          */
376         /*@{*/
377         template<typename T>
378         bool io( const std::string &name, const T &value ) {
379             stream.member( name );
380             io::detail::has_archive_tag<T>::write( stream, value );
381             return false;
382         }
383         template<typename T>
384         bool io( const std::string &name, const T &value, const T &default_value ) {
385             if( value == default_value ) {
386                 return false;
387             }
388             return io( name, value );
389         }
390         template<typename T>
391         bool io( const std::string &name, const T &value, default_tag ) {
392             static const T default_value = T();
393             return io<T>( name, value, default_value );
394         }
395         template<typename T>
396         bool io( const std::string &name, const T &value, empty_default_tag ) {
397             if( !value.empty() ) {
398                 io<T>( name, value );
399             }
400             return false;
401         }
402         /**
403          * Special function to store pointers.
404          *
405          * If the pointer is `nullptr`, it will not be stored at all (`nullptr` is the default value).
406          * Otherwise the save function is called to translate the pointer into a string (which can be
407          * the id of the pointed to object or similar). The returned string is stored in the archive.
408          *
409          * The function signature is compatible with the similar function in the
410          * @ref JsonObjectInputArchive, so it can be used when the archive type is a template parameter.
411          */
412         template<typename T>
413         bool io( const std::string &name, const T *pointer,
414                  const std::function<void( const std::string & )> &,
415                  const std::function<std::string( const T & )> &save, bool required = false ) {
416             if( pointer == nullptr ) {
417                 if( required ) {
418                     throw JsonError( "a required member is null: " + name );
419                 }
420                 return false;
421             }
422             return io( name, save( *pointer ) );
423         }
424         template<typename T>
425         bool io( const std::string &name, const T *pointer,
426                  const std::function<void( const std::string & )> &load,
427                  const std::function<std::string( const T & )> &save, required_tag ) {
428             return io<T>( name, pointer, load, save, true );
429         }
430         /*@}*/
431         /**
432          * For compatibility with the input archive. Output archives obliviously never read anything
433          * and always return false.
434          */
435         template<typename T>
436         bool read( const std::string &, T & ) {
437             return false;
438         }
439 };
440 
441 /**
442  * Output archive matching the input archive @ref JsonArrayInputArchive.
443  *
444  * The class creates a valid JSON value in the output stream (that means it adds the initial
445  * '[' and ']' to the stream).
446  */
447 class JsonArrayOutputArchive
448 {
449     public:
450         using is_input = std::false_type;
451 
452         JsonOut &stream;
453 
454         explicit JsonArrayOutputArchive( JsonOut &stream )
455             : stream( stream ) {
456             stream.start_array();
457         }
458         ~JsonArrayOutputArchive() {
459             stream.end_array();
460         }
461 
462         template<typename T>
463         bool io( const T &value ) {
464             io::detail::has_archive_tag<T>::write( stream, value );
465             return false;
466         }
467 };
468 
469 } // namespace io
470 
471 #endif // CATA_SRC_CATA_IO_H
472