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