Cataclysm-DDA/src/cata_variant.h

536 lines
15 KiB
C++

#pragma once
#ifndef CATA_SRC_CATA_VARIANT_H
#define CATA_SRC_CATA_VARIANT_H
#include <array>
#include <chrono>
#include <cstddef>
#include <functional>
#include <iosfwd>
#include <string>
#include <type_traits>
#include <utility>
#include "character_id.h"
#include "debug.h"
#include "enum_conversions.h"
#include "hash_utils.h"
#include "point.h"
#include "to_string_id.h"
#include "type_id.h"
class JsonOut;
class JsonValue;
template <typename E> struct enum_traits;
enum class mutagen_technique : int;
namespace debug_menu
{
enum class debug_menu_index : int;
} // namespace debug_menu
// cata_variant is a variant-like type that stores a variety of different cata
// types. All types are stored by converting them to a string.
enum class cata_variant_type : int {
void_, // Special type for empty variants
achievement_id,
addiction_id,
bionic_id,
body_part,
bool_,
character_id,
chrono_seconds,
debug_menu_index,
efftype_id,
faction_id,
field_type_id,
field_type_str_id,
furn_id,
furn_str_id,
flag_id,
int_,
item_group_id,
itype_id,
matype_id,
mtype_id,
mongroup_id,
move_mode_id,
mutagen_technique,
mutation_category_id,
nested_mapgen_id,
npc_template_id,
oter_id,
oter_type_str_id,
overmap_special_id,
palette_id,
point,
profession_id,
skill_id,
species_id,
spell_id,
string,
ter_id,
ter_furn_transform_id,
ter_str_id,
trait_id,
trap_id,
trap_str_id,
tripoint,
vgroup_id,
widget_id,
zone_type_id,
num_types, // last
};
template<>
struct enum_traits<cata_variant_type> {
static constexpr cata_variant_type last = cata_variant_type::num_types;
};
// Here follows various implementation details. Skip to the bottom of the file
// to see cata_variant itself.
namespace cata_variant_detail
{
// The convert struct is specialized for each cata_variant_type to provide the
// code for converting that type to or from a string.
template<cata_variant_type Type>
struct convert;
// type_for<T>() returns the cata_variant_type enum value corresponding to the
// given value type. e.g. type_for<mtype_id>() == cata_variant_type::mtype_id.
template<typename T, size_t... I>
constexpr cata_variant_type type_for_impl( std::index_sequence<I...> )
{
constexpr size_t num_types = static_cast<size_t>( cata_variant_type::num_types );
constexpr std::array<bool, num_types> matches = {{
std::is_same<T, typename convert<static_cast<cata_variant_type>( I )>::type>::value...
}
};
for( size_t i = 0; i < num_types; ++i ) {
if( matches[i] ) {
return static_cast<cata_variant_type>( i );
}
}
// No match
return cata_variant_type::num_types;
}
// Inherit from this struct to easily implement convert specializations for any
// string type
template<typename T>
struct convert_string {
using type = T;
static_assert( std::is_same<T, std::string>::value,
"Intended for use only with string typedefs" );
static std::string to_string( const T &v ) {
return v;
}
static T from_string( const std::string &v ) {
return v;
}
static bool is_valid( const std::string & ) {
return true;
}
};
// Inherit from this struct to easily implement convert specializations for any
// string_id type
template<typename T>
struct convert_string_id {
using type = T;
static std::string to_string( const T &v ) {
return v.str();
}
static T from_string( const std::string &v ) {
return T( v );
}
static bool is_valid( const std::string &v ) {
return from_string( v ).is_valid();
}
};
// Inherit from this struct to easily implement convert specializations for any
// int_id type
template<typename T>
struct convert_int_id {
using type = T;
static std::string to_string( const T &v ) {
return v.id().str();
}
static T from_string( const std::string &v ) {
return T( v );
}
static bool is_valid( const std::string &v ) {
return to_string_id_t<T>( v ).is_valid();
}
};
// Inherit from this struct to easily implement convert specializations for any
// enum type
template<typename T>
struct convert_enum {
using type = T;
static std::string to_string( const T &v ) {
return io::enum_to_string( v );
}
static T from_string( const std::string &v ) {
return io::string_to_enum<T>( v );
}
static bool is_valid( const std::string &v ) {
return io::enum_is_valid<T>( v );
}
};
// These are the specializations of convert for each value type.
static_assert( static_cast<int>( cata_variant_type::num_types ) == 47,
"This assert is a reminder to add conversion support for any new types to the "
"below specializations" );
template<>
struct convert<cata_variant_type::void_> {
using type = void;
static bool is_valid( const std::string &s ) {
return s.empty();
}
};
template<>
struct convert<cata_variant_type::achievement_id> : convert_string_id<achievement_id> {};
template<>
struct convert<cata_variant_type::addiction_id> : convert_string_id<addiction_id> {};
template<>
struct convert<cata_variant_type::bionic_id> : convert_string_id<bionic_id> {};
template<>
struct convert<cata_variant_type::body_part> : convert_int_id<bodypart_id> {};
template<>
struct convert<cata_variant_type::bool_> {
using type = bool;
static std::string to_string( const bool v ) {
return std::to_string( static_cast<int>( v ) );
}
static bool from_string( const std::string &v ) {
return std::stoi( v );
}
static bool is_valid( const std::string &v ) {
// TODO: check for int-ness
int i = std::stoi( v );
return i >= 0 && i <= 1;
}
};
template<>
struct convert<cata_variant_type::character_id> {
using type = character_id;
static std::string to_string( const character_id &v ) {
return std::to_string( v.get_value() );
}
static character_id from_string( const std::string &v ) {
return character_id( std::stoi( v ) );
}
static bool is_valid( const std::string & ) {
// TODO: check for int-ness
return true;
}
};
template<>
struct convert<cata_variant_type::chrono_seconds> {
using type = std::chrono::seconds;
static std::string to_string( const std::chrono::seconds &v ) {
return std::to_string( v.count() );
}
static std::chrono::seconds from_string( const std::string &v ) {
return std::chrono::seconds( std::stoll( v ) );
}
static bool is_valid( const std::string & ) {
// TODO: check for int-ness
return true;
}
};
template<>
struct convert<cata_variant_type::debug_menu_index> : convert_enum<debug_menu::debug_menu_index> {};
template<>
struct convert<cata_variant_type::move_mode_id> : convert_string_id<move_mode_id> {};
template<>
struct convert<cata_variant_type::efftype_id> : convert_string_id<efftype_id> {};
template<>
struct convert<cata_variant_type::faction_id> : convert_string_id<faction_id> {};
template<>
struct convert<cata_variant_type::field_type_id> : convert_int_id<field_type_id> {};
template<>
struct convert<cata_variant_type::field_type_str_id> : convert_string_id<field_type_str_id> {};
template<>
struct convert<cata_variant_type::furn_id> : convert_int_id<furn_id> {};
template<>
struct convert<cata_variant_type::furn_str_id> : convert_string_id<furn_str_id> {};
template<>
struct convert<cata_variant_type::flag_id> : convert_string_id<flag_id> {};
template<>
struct convert<cata_variant_type::int_> {
using type = int;
static std::string to_string( const int v ) {
return std::to_string( v );
}
static int from_string( const std::string &v ) {
return std::stoi( v );
}
static bool is_valid( const std::string & ) {
// TODO: check for int-ness
return true;
}
};
template<>
struct convert<cata_variant_type::item_group_id> : convert_string_id<item_group_id> {};
template<>
struct convert<cata_variant_type::itype_id> : convert_string_id<itype_id> {};
template<>
struct convert<cata_variant_type::matype_id> : convert_string_id<matype_id> {};
template<>
struct convert<cata_variant_type::mtype_id> : convert_string_id<mtype_id> {};
template<>
struct convert<cata_variant_type::mongroup_id> : convert_string_id<mongroup_id> {};
template<>
struct convert<cata_variant_type::mutagen_technique> : convert_enum<mutagen_technique> {};
template<>
struct convert<cata_variant_type::mutation_category_id> :
convert_string_id<mutation_category_id> {};
template<>
struct convert<cata_variant_type::nested_mapgen_id> : convert_string_id<nested_mapgen_id> {};
template<>
struct convert<cata_variant_type::npc_template_id> : convert_string_id<npc_template_id> {};
template<>
struct convert<cata_variant_type::oter_id> : convert_int_id<oter_id> {};
template<>
struct convert<cata_variant_type::oter_type_str_id> : convert_string_id<oter_type_str_id> {};
template<>
struct convert<cata_variant_type::overmap_special_id> : convert_string_id<overmap_special_id> {};
template<>
struct convert<cata_variant_type::palette_id> : convert_string_id<palette_id> {};
template<>
struct convert<cata_variant_type::point> {
using type = point;
static std::string to_string( const point &v ) {
return v.to_string();
}
static point from_string( const std::string &v ) {
return point::from_string( v );
}
static bool is_valid( const std::string & ) {
// TODO: check for point-ness
return true;
}
};
template<>
struct convert<cata_variant_type::profession_id> : convert_string_id<profession_id> {};
template<>
struct convert<cata_variant_type::skill_id> : convert_string_id<skill_id> {};
template<>
struct convert<cata_variant_type::species_id> : convert_string_id<species_id> {};
template<>
struct convert<cata_variant_type::spell_id> : convert_string_id<spell_id> {};
template<>
struct convert<cata_variant_type::string> : convert_string<std::string> {};
template<>
struct convert<cata_variant_type::ter_id> : convert_int_id<ter_id> {};
template<>
struct convert<cata_variant_type::ter_furn_transform_id> :
convert_string_id<ter_furn_transform_id> {};
template<>
struct convert<cata_variant_type::ter_str_id> : convert_string_id<ter_str_id> {};
template<>
struct convert<cata_variant_type::trait_id> : convert_string_id<trait_id> {};
template<>
struct convert<cata_variant_type::trap_id> : convert_int_id<trap_id> {};
template<>
struct convert<cata_variant_type::trap_str_id> : convert_string_id<trap_str_id> {};
template<>
struct convert<cata_variant_type::tripoint> {
using type = tripoint;
static std::string to_string( const tripoint &v ) {
return v.to_string();
}
static tripoint from_string( const std::string &v ) {
return tripoint::from_string( v );
}
static bool is_valid( const std::string & ) {
// TODO: check for tripoint-ness
return true;
}
};
template<>
struct convert<cata_variant_type::vgroup_id> : convert_string_id<vgroup_id> {};
template<>
struct convert<cata_variant_type::widget_id> : convert_string_id<widget_id> {};
template<>
struct convert<cata_variant_type::zone_type_id> : convert_string_id<zone_type_id> {};
} // namespace cata_variant_detail
template<typename T>
constexpr cata_variant_type cata_variant_type_for()
{
constexpr size_t num_types = static_cast<size_t>( cata_variant_type::num_types );
using SimpleT = std::remove_cv_t<std::remove_reference_t<T>>;
return cata_variant_detail::type_for_impl<SimpleT>( std::make_index_sequence<num_types> {} );
}
class cata_variant
{
public:
// Default constructor for an 'empty' variant (you can't get a value
// from it).
cata_variant() : type_( cata_variant_type::void_ ) {}
// Constructor that attempts to infer the type from the type of the
// value passed.
template < typename Value,
typename = std::enable_if_t <(
cata_variant_type_for<Value>() < cata_variant_type::num_types )>>
explicit cata_variant( Value && value ) {
constexpr cata_variant_type Type = cata_variant_type_for<Value>();
type_ = Type;
value_ =
cata_variant_detail::convert<Type>::to_string( std::forward<Value>( value ) );
}
// Call this function to unambiguously construct a cata_variant instance,
// pass the type as a template parameter.
// In cases where the value type alone is unambiguous, the above
// simpler constructor call can be used.
template<cata_variant_type Type, typename Value>
static cata_variant make( Value &&value ) {
return cata_variant(
Type, cata_variant_detail::convert<Type>::to_string(
std::forward<Value>( value ) ) );
}
// Call this to construct from a type + string. This should rarely be
// necessary, so think twice before using it (that's why the equivalent
// constructor is private).
static cata_variant from_string( cata_variant_type t, std::string &&v ) {
return cata_variant( t, std::move( v ) );
}
cata_variant_type type() const {
return type_;
}
template<cata_variant_type Type>
auto get() const -> typename cata_variant_detail::convert<Type>::type {
if( type_ != Type ) {
debugmsg( "Tried to extract type %s from cata_variant which contained %s",
io::enum_to_string( Type ),
io::enum_to_string( type_ ) );
return {};
}
return cata_variant_detail::convert<Type>::from_string( value_ );
}
template<typename T>
T get() const {
return get<cata_variant_type_for<T>()>();
}
const std::string &get_string() const {
return value_;
}
bool is_valid() const;
std::pair<cata_variant_type, std::string> as_pair() const {
return std::make_pair( type_, value_ );
}
void serialize( JsonOut & ) const;
void deserialize( const JsonValue &jsin );
#define CATA_VARIANT_OPERATOR(op) \
friend bool operator op( const cata_variant &l, const cata_variant &r ) { \
return l.as_pair() op r.as_pair(); \
}
CATA_VARIANT_OPERATOR( == )
CATA_VARIANT_OPERATOR( != )
CATA_VARIANT_OPERATOR( < ) // NOLINT( cata-use-localized-sorting )
CATA_VARIANT_OPERATOR( <= ) // NOLINT( cata-use-localized-sorting )
CATA_VARIANT_OPERATOR( > ) // NOLINT( cata-use-localized-sorting )
CATA_VARIANT_OPERATOR( >= ) // NOLINT( cata-use-localized-sorting )
#undef CATA_VARIANT_OPERATOR
private:
explicit cata_variant( cata_variant_type t, std::string &&v )
: type_( t )
, value_( std::move( v ) )
{}
cata_variant_type type_;
std::string value_;
};
namespace std
{
template<>
struct hash<cata_variant_type> {
size_t operator()( const cata_variant_type v ) const noexcept {
return static_cast<size_t>( v );
}
};
template<>
struct hash<cata_variant> {
size_t operator()( const cata_variant &v ) const noexcept {
return cata::tuple_hash()( v.as_pair() );
}
};
} // namespace std
#endif // CATA_SRC_CATA_VARIANT_H