1 #pragma once 2 #ifndef CATA_SRC_CATA_VARIANT_H 3 #define CATA_SRC_CATA_VARIANT_H 4 5 #include <array> 6 #include <chrono> 7 #include <cstddef> 8 #include <functional> 9 #include <iosfwd> 10 #include <string> 11 #include <type_traits> 12 #include <utility> 13 14 #include "character_id.h" 15 #include "debug.h" 16 #include "enum_conversions.h" 17 #include "hash_utils.h" 18 #include "pldata.h" 19 #include "point.h" 20 #include "to_string_id.h" 21 #include "type_id.h" 22 23 class JsonIn; 24 class JsonOut; 25 template <typename E> struct enum_traits; 26 27 enum class mutagen_technique : int; 28 29 namespace debug_menu 30 { 31 enum class debug_menu_index : int; 32 } // namespace debug_menu 33 34 // cata_variant is a variant-like type that stores a variety of different cata 35 // types. All types are stored by converting them to a string. 36 37 enum class cata_variant_type : int { 38 void_, // Special type for empty variants 39 achievement_id, 40 add_type, 41 bionic_id, 42 body_part, 43 bool_, 44 character_id, 45 chrono_seconds, 46 debug_menu_index, 47 efftype_id, 48 furn_id, 49 furn_str_id, 50 flag_id, 51 int_, 52 itype_id, 53 matype_id, 54 mtype_id, 55 move_mode_id, 56 mutagen_technique, 57 mutation_category_id, 58 oter_id, 59 oter_type_str_id, 60 point, 61 profession_id, 62 skill_id, 63 species_id, 64 spell_id, 65 string, 66 ter_id, 67 ter_str_id, 68 trait_id, 69 trap_str_id, 70 tripoint, 71 num_types, // last 72 }; 73 74 template<> 75 struct enum_traits<cata_variant_type> { 76 static constexpr cata_variant_type last = cata_variant_type::num_types; 77 }; 78 79 // Here follows various implementation details. Skip to the bottom of the file 80 // to see cata_variant itself. 81 82 namespace cata_variant_detail 83 { 84 85 // The convert struct is specialized for each cata_variant_type to provide the 86 // code for converting that type to or from a string. 87 template<cata_variant_type Type> 88 struct convert; 89 90 // type_for<T>() returns the cata_variant_type enum value corresponding to the 91 // given value type. e.g. type_for<mtype_id>() == cata_variant_type::mtype_id. 92 93 template<typename T, size_t... I> 94 constexpr cata_variant_type type_for_impl( std::index_sequence<I...> ) 95 { 96 constexpr size_t num_types = static_cast<size_t>( cata_variant_type::num_types ); 97 constexpr std::array<bool, num_types> matches = {{ 98 std::is_same<T, typename convert<static_cast<cata_variant_type>( I )>::type>::value... 99 } 100 }; 101 for( size_t i = 0; i < num_types; ++i ) { 102 if( matches[i] ) { 103 return static_cast<cata_variant_type>( i ); 104 } 105 } 106 // No match 107 return cata_variant_type::num_types; 108 } 109 110 // Inherit from this struct to easily implement convert specializations for any 111 // string type 112 template<typename T> 113 struct convert_string { 114 using type = T; 115 static_assert( std::is_same<T, std::string>::value, 116 "Intended for use only with string typedefs" ); 117 static std::string to_string( const T &v ) { 118 return v; 119 } 120 static T from_string( const std::string &v ) { 121 return v; 122 } 123 static bool is_valid( const std::string & ) { 124 return true; 125 } 126 }; 127 128 // Inherit from this struct to easily implement convert specializations for any 129 // string_id type 130 template<typename T> 131 struct convert_string_id { 132 using type = T; 133 static std::string to_string( const T &v ) { 134 return v.str(); 135 } 136 static T from_string( const std::string &v ) { 137 return T( v ); 138 } 139 static bool is_valid( const std::string &v ) { 140 return from_string( v ).is_valid(); 141 } 142 }; 143 144 // Inherit from this struct to easily implement convert specializations for any 145 // int_id type 146 template<typename T> 147 struct convert_int_id { 148 using type = T; 149 static std::string to_string( const T &v ) { 150 return v.id().str(); 151 } 152 static T from_string( const std::string &v ) { 153 return T( v ); 154 } 155 static bool is_valid( const std::string &v ) { 156 return to_string_id_t<T>( v ).is_valid(); 157 } 158 }; 159 160 // Inherit from this struct to easily implement convert specializations for any 161 // enum type 162 template<typename T> 163 struct convert_enum { 164 using type = T; 165 static std::string to_string( const T &v ) { 166 return io::enum_to_string( v ); 167 } 168 static T from_string( const std::string &v ) { 169 return io::string_to_enum<T>( v ); 170 } 171 static bool is_valid( const std::string &v ) { 172 return io::enum_is_valid<T>( v ); 173 } 174 }; 175 176 // These are the specializations of convert for each value type. 177 static_assert( static_cast<int>( cata_variant_type::num_types ) == 33, 178 "This assert is a reminder to add conversion support for any new types to the " 179 "below specializations" ); 180 181 template<> 182 struct convert<cata_variant_type::void_> { 183 using type = void; 184 static bool is_valid( const std::string &s ) { 185 return s.empty(); 186 } 187 }; 188 189 template<> 190 struct convert<cata_variant_type::achievement_id> : convert_string_id<achievement_id> {}; 191 192 template<> 193 struct convert<cata_variant_type::add_type> : convert_enum<add_type> {}; 194 195 template<> 196 struct convert<cata_variant_type::bionic_id> : convert_string_id<bionic_id> {}; 197 198 template<> 199 struct convert<cata_variant_type::body_part> : convert_int_id<bodypart_id> {}; 200 201 template<> 202 struct convert<cata_variant_type::bool_> { 203 using type = bool; 204 static std::string to_string( const bool v ) { 205 return std::to_string( static_cast<int>( v ) ); 206 } 207 static bool from_string( const std::string &v ) { 208 return std::stoi( v ); 209 } 210 static bool is_valid( const std::string &v ) { 211 // TODO: check for int-ness 212 int i = std::stoi( v ); 213 return i >= 0 && i <= 1; 214 } 215 }; 216 217 template<> 218 struct convert<cata_variant_type::character_id> { 219 using type = character_id; 220 static std::string to_string( const character_id &v ) { 221 return std::to_string( v.get_value() ); 222 } 223 static character_id from_string( const std::string &v ) { 224 return character_id( std::stoi( v ) ); 225 } 226 static bool is_valid( const std::string & ) { 227 // TODO: check for int-ness 228 return true; 229 } 230 }; 231 232 template<> 233 struct convert<cata_variant_type::chrono_seconds> { 234 using type = std::chrono::seconds; 235 static std::string to_string( const std::chrono::seconds &v ) { 236 return std::to_string( v.count() ); 237 } 238 static std::chrono::seconds from_string( const std::string &v ) { 239 return std::chrono::seconds( std::stoll( v ) ); 240 } 241 static bool is_valid( const std::string & ) { 242 // TODO: check for int-ness 243 return true; 244 } 245 }; 246 247 template<> 248 struct convert<cata_variant_type::debug_menu_index> : convert_enum<debug_menu::debug_menu_index> {}; 249 250 template<> 251 struct convert<cata_variant_type::move_mode_id> : convert_string_id<move_mode_id> {}; 252 253 template<> 254 struct convert<cata_variant_type::efftype_id> : convert_string_id<efftype_id> {}; 255 256 template<> 257 struct convert<cata_variant_type::furn_id> : convert_int_id<furn_id> {}; 258 259 template<> 260 struct convert<cata_variant_type::furn_str_id> : convert_string_id<furn_str_id> {}; 261 262 template<> 263 struct convert<cata_variant_type::flag_id> : convert_string_id<flag_id> {}; 264 265 template<> 266 struct convert<cata_variant_type::int_> { 267 using type = int; 268 static std::string to_string( const int v ) { 269 return std::to_string( v ); 270 } 271 static int from_string( const std::string &v ) { 272 return std::stoi( v ); 273 } 274 static bool is_valid( const std::string & ) { 275 // TODO: check for int-ness 276 return true; 277 } 278 }; 279 280 template<> 281 struct convert<cata_variant_type::itype_id> : convert_string_id<itype_id> {}; 282 283 template<> 284 struct convert<cata_variant_type::matype_id> : convert_string_id<matype_id> {}; 285 286 template<> 287 struct convert<cata_variant_type::mtype_id> : convert_string_id<mtype_id> {}; 288 289 template<> 290 struct convert<cata_variant_type::mutagen_technique> : convert_enum<mutagen_technique> {}; 291 292 template<> 293 struct convert<cata_variant_type::mutation_category_id> : 294 convert_string_id<mutation_category_id> {}; 295 296 template<> 297 struct convert<cata_variant_type::oter_id> : convert_int_id<oter_id> {}; 298 299 template<> 300 struct convert<cata_variant_type::oter_type_str_id> : convert_string_id<oter_type_str_id> {}; 301 302 template<> 303 struct convert<cata_variant_type::point> { 304 using type = point; 305 static std::string to_string( const point &v ) { 306 return v.to_string(); 307 } 308 static point from_string( const std::string &v ) { 309 return point::from_string( v ); 310 } 311 static bool is_valid( const std::string & ) { 312 // TODO: check for point-ness 313 return true; 314 } 315 }; 316 317 template<> 318 struct convert<cata_variant_type::profession_id> : convert_string_id<profession_id> {}; 319 320 template<> 321 struct convert<cata_variant_type::skill_id> : convert_string_id<skill_id> {}; 322 323 template<> 324 struct convert<cata_variant_type::species_id> : convert_string_id<species_id> {}; 325 326 template<> 327 struct convert<cata_variant_type::spell_id> : convert_string_id<spell_id> {}; 328 329 template<> 330 struct convert<cata_variant_type::string> : convert_string<std::string> {}; 331 332 template<> 333 struct convert<cata_variant_type::ter_id> : convert_int_id<ter_id> {}; 334 335 template<> 336 struct convert<cata_variant_type::ter_str_id> : convert_string_id<ter_str_id> {}; 337 338 template<> 339 struct convert<cata_variant_type::trait_id> : convert_string_id<trait_id> {}; 340 341 template<> 342 struct convert<cata_variant_type::trap_str_id> : convert_string_id<trap_str_id> {}; 343 344 template<> 345 struct convert<cata_variant_type::tripoint> { 346 using type = tripoint; 347 static std::string to_string( const tripoint &v ) { 348 return v.to_string(); 349 } 350 static tripoint from_string( const std::string &v ) { 351 return tripoint::from_string( v ); 352 } 353 static bool is_valid( const std::string & ) { 354 // TODO: check for tripoint-ness 355 return true; 356 } 357 }; 358 359 } // namespace cata_variant_detail 360 361 template<typename T> 362 constexpr cata_variant_type cata_variant_type_for() 363 { 364 constexpr size_t num_types = static_cast<size_t>( cata_variant_type::num_types ); 365 using SimpleT = std::remove_cv_t<std::remove_reference_t<T>>; 366 return cata_variant_detail::type_for_impl<SimpleT>( std::make_index_sequence<num_types> {} ); 367 } 368 369 class cata_variant 370 { 371 public: 372 // Default constructor for an 'empty' variant (you can't get a value 373 // from it). 374 cata_variant() : type_( cata_variant_type::void_ ) {} 375 376 // Constructor that attempts to infer the type from the type of the 377 // value passed. 378 template < typename Value, 379 typename = std::enable_if_t <( 380 cata_variant_type_for<Value>() < cata_variant_type::num_types )>> 381 explicit cata_variant( Value && value ) { 382 constexpr cata_variant_type Type = cata_variant_type_for<Value>(); 383 type_ = Type; 384 value_ = 385 cata_variant_detail::convert<Type>::to_string( std::forward<Value>( value ) ); 386 } 387 388 // Call this function to unambiguously construct a cata_variant instance, 389 // pass the type as a template parameter. 390 // In cases where the value type alone is unambiguous, the above 391 // simpler constructor call can be used. 392 template<cata_variant_type Type, typename Value> 393 static cata_variant make( Value &&value ) { 394 return cata_variant( 395 Type, cata_variant_detail::convert<Type>::to_string( 396 std::forward<Value>( value ) ) ); 397 } 398 399 // Call this to construct from a type + string. This should rarely be 400 // necessary, so think twice before using it (that's why the equivalent 401 // constructor is private). 402 static cata_variant from_string( cata_variant_type t, std::string &&v ) { 403 return cata_variant( t, std::move( v ) ); 404 } 405 406 cata_variant_type type() const { 407 return type_; 408 } 409 410 template<cata_variant_type Type> 411 auto get() const -> typename cata_variant_detail::convert<Type>::type { 412 if( type_ != Type ) { 413 debugmsg( "Tried to extract type %s from cata_variant which contained %s", 414 io::enum_to_string( Type ), 415 io::enum_to_string( type_ ) ); 416 return {}; 417 } 418 return cata_variant_detail::convert<Type>::from_string( value_ ); 419 } 420 421 template<typename T> 422 T get() const { 423 return get<cata_variant_type_for<T>()>(); 424 } 425 426 const std::string &get_string() const { 427 return value_; 428 } 429 430 bool is_valid() const; 431 432 std::pair<cata_variant_type, std::string> as_pair() const { 433 return std::make_pair( type_, value_ ); 434 } 435 436 void serialize( JsonOut & ) const; 437 void deserialize( JsonIn & ); 438 439 #define CATA_VARIANT_OPERATOR(op) \ 440 friend bool operator op( const cata_variant &l, const cata_variant &r ) { \ 441 return l.as_pair() op r.as_pair(); \ 442 } 443 CATA_VARIANT_OPERATOR( == ) 444 CATA_VARIANT_OPERATOR( != ) 445 CATA_VARIANT_OPERATOR( < ) // NOLINT( cata-use-localized-sorting ) 446 CATA_VARIANT_OPERATOR( <= ) // NOLINT( cata-use-localized-sorting ) 447 CATA_VARIANT_OPERATOR( > ) // NOLINT( cata-use-localized-sorting ) 448 CATA_VARIANT_OPERATOR( >= ) // NOLINT( cata-use-localized-sorting ) 449 #undef CATA_VARIANT_OPERATOR 450 private: 451 explicit cata_variant( cata_variant_type t, std::string &&v ) 452 : type_( t ) 453 , value_( std::move( v ) ) 454 {} 455 456 cata_variant_type type_; 457 std::string value_; 458 }; 459 460 namespace std 461 { 462 463 template<> 464 struct hash<cata_variant_type> { 465 size_t operator()( const cata_variant_type v ) const noexcept { 466 return static_cast<size_t>( v ); 467 } 468 }; 469 470 template<> 471 struct hash<cata_variant> { 472 size_t operator()( const cata_variant &v ) const noexcept { 473 return cata::tuple_hash()( v.as_pair() ); 474 } 475 }; 476 477 } // namespace std 478 479 #endif // CATA_SRC_CATA_VARIANT_H 480