Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cata_variant enhancements #33697

Merged
merged 10 commits into from
Sep 1, 2019
88 changes: 24 additions & 64 deletions src/cata_variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,78 +2,38 @@

#include <unordered_map>

namespace cata_variant_detail
namespace io
{

std::string to_string( cata_variant_type type )
template<>
std::string enum_to_string<cata_variant_type>( cata_variant_type type )
{
switch( type ) {
case cata_variant_type::add_type:
return "add_type";
case cata_variant_type::bionic_id:
return "bionic_id";
case cata_variant_type::body_part:
return "body_part";
case cata_variant_type::bool_:
return "bool";
case cata_variant_type::character_id:
return "character_id";
case cata_variant_type::efftype_id:
return "efftype_id";
case cata_variant_type::int_:
return "int";
case cata_variant_type::itype_id:
return "itype_id";
case cata_variant_type::matype_id:
return "matype_id";
case cata_variant_type::mtype_id:
return "mtype_id";
case cata_variant_type::mutagen_technique:
return "mutagen_technique";
case cata_variant_type::mutation_category_id:
return "mutation_category_id";
case cata_variant_type::oter_id:
return "oter_id";
case cata_variant_type::skill_id:
return "skill_id";
case cata_variant_type::string:
return "string";
case cata_variant_type::trait_id:
return "trait_id";
case cata_variant_type::trap_str_id:
return "trap_str_id";
// *INDENT-OFF*
case cata_variant_type::void_: return "void";
case cata_variant_type::add_type: return "add_type";
case cata_variant_type::bionic_id: return "bionic_id";
case cata_variant_type::body_part: return "body_part";
case cata_variant_type::bool_: return "bool";
case cata_variant_type::character_id: return "character_id";
case cata_variant_type::efftype_id: return "efftype_id";
case cata_variant_type::int_: return "int";
case cata_variant_type::itype_id: return "itype_id";
case cata_variant_type::matype_id: return "matype_id";
case cata_variant_type::mtype_id: return "mtype_id";
case cata_variant_type::mutagen_technique: return "mutagen_technique";
case cata_variant_type::mutation_category_id: return "mutation_category_id";
case cata_variant_type::oter_id: return "oter_id";
case cata_variant_type::skill_id: return "skill_id";
case cata_variant_type::string: return "string";
case cata_variant_type::trait_id: return "trait_id";
case cata_variant_type::trap_str_id: return "trap_str_id";
// *INDENT-ON*
case cata_variant_type::num_types:
break;
};
debugmsg( "unknown cata_variant_type %d", static_cast<int>( type ) );
return "";
}

static std::unordered_map<std::string, cata_variant_type> create_type_lookup_map()
{
std::unordered_map<std::string, cata_variant_type> result;
int num_types = static_cast<int>( cata_variant_type::num_types );
for( int i = 0; i < num_types; ++i ) {
cata_variant_type type = static_cast<cata_variant_type>( i );
std::string type_as_string = to_string( type );
bool inserted = result.insert( {type_as_string, type} ).second;
if( !inserted ) {
debugmsg( "Duplicate variant type name %s", type_as_string );
}
}
return result;
}

cata_variant_type from_string( const std::string &s )
{
static const std::unordered_map<std::string, cata_variant_type> type_lookup_map =
create_type_lookup_map();
auto it = type_lookup_map.find( s );
if( it == type_lookup_map.end() ) {
debugmsg( "Unexpected cata_variant_type name %s", s );
assert( false );
}
return it->second;
}

} // namespace cata_variant_detail
} // namespace io
67 changes: 58 additions & 9 deletions src/cata_variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "character_id.h"
#include "debug.h"
#include "enum_conversions.h"
#include "enum_traits.h"
#include "hash_utils.h"
#include "type_id.h"

enum add_type : int;
Expand All @@ -21,6 +23,7 @@ using itype_id = std::string;
// types. All types are stored by converting them to a string.

enum class cata_variant_type : int {
void_, // Special type for empty variants
add_type,
bionic_id,
body_part,
Expand All @@ -41,17 +44,17 @@ enum class cata_variant_type : int {
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
{

// Converting cata_variant_type enum values to and from string for serialization and error
// reporting.
std::string to_string( cata_variant_type );
cata_variant_type from_string( const std::string & );

// 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>
Expand Down Expand Up @@ -81,7 +84,7 @@ template<typename T>
constexpr cata_variant_type type_for()
{
constexpr size_t num_types = static_cast<size_t>( cata_variant_type::num_types );
using SimpleT = std::remove_reference_t<T>;
using SimpleT = std::remove_cv_t<std::remove_reference_t<T>>;
return type_for_impl<SimpleT>( std::make_index_sequence<num_types> {} );
}

Expand Down Expand Up @@ -140,10 +143,15 @@ struct convert_enum {
};

// These are the specializations of convert for each value type.
static_assert( static_cast<int>( cata_variant_type::num_types ) == 17,
static_assert( static_cast<int>( cata_variant_type::num_types ) == 18,
"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;
};

template<>
struct convert<cata_variant_type::add_type> : convert_enum<add_type> {};

Expand Down Expand Up @@ -224,6 +232,10 @@ struct convert<cata_variant_type::trap_str_id> : convert_string_id<trap_str_id>
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,
Expand Down Expand Up @@ -255,8 +267,8 @@ class cata_variant
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",
cata_variant_detail::to_string( Type ),
cata_variant_detail::to_string( type_ ) );
io::enum_to_string( Type ),
io::enum_to_string( type_ ) );
return {};
}
return cata_variant_detail::convert<Type>::from_string( value_ );
Expand All @@ -271,6 +283,24 @@ class cata_variant
return value_;
}

std::pair<cata_variant_type, std::string> as_pair() const {
return std::make_pair( type_, value_ );
}

void serialize( JsonOut & ) const;
void deserialize( JsonIn & );

#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( < );
CATA_VARIANT_OPERATOR( <= );
CATA_VARIANT_OPERATOR( > );
CATA_VARIANT_OPERATOR( >= );
#undef CATA_VARIANT_OPERATOR
private:
explicit cata_variant( cata_variant_type t, std::string &&v )
: type_( t )
Expand All @@ -281,4 +311,23 @@ class cata_variant
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_VARIANT_H
2 changes: 1 addition & 1 deletion src/effect.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include "calendar.h"
#include "string_id.h"
#include "translations.h"
#include "tuple_hash.h"
#include "hash_utils.h"
#include "type_id.h"

class player;
Expand Down
5 changes: 3 additions & 2 deletions src/enum_conversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class InvalidEnumString : public std::runtime_error
};

template<typename E>
std::string enum_to_string( E data );
std::string enum_to_string( E );

template<typename E>
std::unordered_map<std::string, E> build_enum_lookup_map()
Expand Down Expand Up @@ -67,7 +67,8 @@ inline E string_to_enum_look_up( const C &container, const std::string &data )
{
const auto iter = container.find( data );
if( iter == container.end() ) {
throw InvalidEnumString{};
throw InvalidEnumString( "Invalid enum string '" + data + "' for '" +
typeid( E ).name() + "'" );
}
return iter->second;
}
Expand Down
12 changes: 6 additions & 6 deletions src/tuple_hash.h → src/hash_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
#ifndef CATA_TUPLE_HASH_H
#define CATA_TUPLE_HASH_H

// Support for tuple and pair hashing.
#include <functional>

// Support for hashing standard types.
// This is taken almost directly from the boost library code.
// Function has to live in the std namespace
// so that it is picked up by argument-dependent name lookup (ADL).
namespace cata
{

Expand All @@ -15,10 +15,10 @@ namespace cata
// See Mike Seymour in magic-numbers-in-boosthash-combine:
// http://stackoverflow.com/questions/4948780

template <class T>
inline void hash_combine( std::size_t &seed, const T &v )
template <class T, typename Hash = std::hash<T>>
inline void hash_combine( std::size_t &seed, const T &v, const Hash &hash = std::hash<T>() )
{
seed ^= std::hash<T>()( v ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 );
seed ^= hash( v ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 );
}

namespace tuple_hash_detail
Expand Down
10 changes: 10 additions & 0 deletions src/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,16 @@ class JsonIn
}
}

template<typename T, std::enable_if_t<std::is_enum<T>::value, int> = 0>
bool read( T &val ) {
std::string s;
if( !read( s ) ) {
return false;
}
val = io::string_to_enum<T>( s );
return true;
}

// array ~> vector, deque, list
template < typename T, typename std::enable_if <
!std::is_same<void, typename T::value_type>::value >::type * = nullptr
Expand Down
2 changes: 1 addition & 1 deletion src/magic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ std::string spell::name() const

std::string spell::message() const
{
if( alt_message.empty() == false ) {
if( !alt_message.empty() ) {
return alt_message.translated();
}
return type->message.translated();
Expand Down
2 changes: 1 addition & 1 deletion src/mutation.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include "character.h"
#include "damage.h"
#include "string_id.h"
#include "tuple_hash.h"
#include "hash_utils.h"
#include "translations.h"
#include "type_id.h"
#include "point.h"
Expand Down
2 changes: 1 addition & 1 deletion src/savegame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#include "overmap.h"
#include "scent_map.h"
#include "translations.h"
#include "tuple_hash.h"
#include "hash_utils.h"
#include "basecamp.h"
#include "json.h"
#include "omdata.h"
Expand Down
17 changes: 17 additions & 0 deletions src/savegame_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3295,6 +3295,23 @@ void kill_tracker::deserialize( JsonIn &jsin )
}
}

void cata_variant::serialize( JsonOut &jsout ) const
{
jsout.start_array();
jsout.write_as_string( type_ );
jsout.write( value_ );
jsout.end_array();
}

void cata_variant::deserialize( JsonIn &jsin )
{
jsin.start_array();
if( !( jsin.read( type_ ) && jsin.read( value_ ) ) ) {
jsin.error( "Failed to read cata_variant" );
}
jsin.end_array();
}

void submap::store( JsonOut &jsout ) const
{
jsout.member( "turn_last_touched", last_touched );
Expand Down
Loading