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