diff --git a/src/math_parser.cpp b/src/math_parser.cpp index fec22c0ad58ab..f107557557e44 100644 --- a/src/math_parser.cpp +++ b/src/math_parser.cpp @@ -204,7 +204,7 @@ constexpr void _validate_operand( thingie const &thing, std::string_view symbol void _validate_unused_kwargs( diag_kwargs const &kwargs ) { - for( diag_kwargs::value_type const &v : kwargs ) { + for( diag_kwargs::impl_t::value_type const &v : kwargs.kwargs ) { if( !v.second.was_used() ) { throw std::invalid_argument( string_format( R"(Unused kwarg "%s")", v.first ) ); } @@ -601,7 +601,7 @@ void math_exp::math_exp_impl::new_func() "All positional arguments must precede keyword-value pairs" ); } kwarg &kw = std::get( output.top().data ); - kwargs.emplace( kw.key, _get_diag_value( *kw.val ) ); + kwargs.kwargs.emplace( kw.key, _get_diag_value( *kw.val ) ); output.pop(); } for( std::vector::size_type i = 0; i < nparams; i++ ) { diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index 7160330e22e01..38659ca9f902f 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -31,10 +31,7 @@ The typical parsing function takes the form: std::function myfunction_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value myval( std::string{} ); - if( kwargs.count( "mykwarg" ) != 0 ) { - myval = *kwargs.at( "mykwarg" ); - } + diag_value myval = kwargs.kwarg_or( "mykwarg", "default-value" ); ...parse-time code... @@ -198,10 +195,7 @@ std::function damage_level_eval( char scope, std::function effect_intensity_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value bp_val( std::string{} ); - if( kwargs.count( "bodypart" ) != 0 ) { - bp_val = *kwargs.at( "bodypart" ); - } + diag_value bp_val = kwargs.kwarg_or( "bodypart" ); return[effect_id = params[0], bp_val, beta = is_beta( scope )]( dialogue const & d ) { std::string const bp_str = bp_val.str( d ); bodypart_id const bp = bp_str.empty() ? bodypart_str_id::NULL_ID() : bodypart_id( bp_str ); @@ -349,8 +343,10 @@ std::function field_strength_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { std::optional loc_var; - if( kwargs.count( "location" ) != 0 ) { - loc_var = kwargs.at( "location" )->var(); + diag_value loc_val = kwargs.kwarg_or( "location" ); + + if( !loc_val.is_empty() ) { + loc_var = loc_val.var(); } else if( scope == 'g' ) { throw std::invalid_argument( string_format( R"("field_strength" needs either an actor scope (u/n) or a 'location' kwarg)" ) ); @@ -402,10 +398,7 @@ std::function sum_traits_of_category_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value type( std::string{ "ALL" } ); - if( kwargs.count( "type" ) != 0 ) { - type = *kwargs.at( "type" ); - } + diag_value type = kwargs.kwarg_or( "type", "ALL" ); return [beta = is_beta( scope ), category = params[0], type]( dialogue const & d ) { @@ -432,10 +425,7 @@ std::function sum_traits_of_category_char_has_eval( char s std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value type( std::string{ "ALL" } ); - if( kwargs.count( "type" ) != 0 ) { - type = *kwargs.at( "type" ); - } + diag_value type = kwargs.kwarg_or( "type", "ALL" ); return [beta = is_beta( scope ), category = params[0], type]( dialogue const & d ) { @@ -539,29 +529,20 @@ std::function spellcasting_adjustment_ass( char scop scope_school, scope_spell }; - diag_value filter( std::string{} ); + diag_value filter; spell_scope spellsearch_scope; - if( kwargs.count( "mod" ) != 0 ) { - filter = *kwargs.at( "mod" ); + if( filter = kwargs.kwarg_or( "mod" ); !filter.is_empty() ) { spellsearch_scope = scope_mod; - } else if( kwargs.count( "school" ) != 0 ) { - filter = *kwargs.at( "school" ); + } else if( filter = kwargs.kwarg_or( "school" ); !filter.is_empty() ) { spellsearch_scope = scope_school; - } else if( kwargs.count( "spell" ) != 0 ) { - filter = *kwargs.at( "spell" ); + } else if( filter = kwargs.kwarg_or( "spell" ); !filter.is_empty() ) { spellsearch_scope = scope_spell; } else { spellsearch_scope = scope_all; } - diag_value whitelist( std::string{} ); - diag_value blacklist( std::string{} ); - if( kwargs.count( "flag_whitelist" ) != 0 ) { - whitelist = *kwargs.at( "flag_whitelist" ); - } - if( kwargs.count( "flag_blacklist" ) != 0 ) { - blacklist = *kwargs.at( "flag_blacklist" ); - } + diag_value whitelist = kwargs.kwarg_or( "flag_whitelist" ); + diag_value blacklist = kwargs.kwarg_or( "flag_blacklist" ); return[beta = is_beta( scope ), spellcasting_property = params[0], whitelist, blacklist, spellsearch_scope, @@ -629,10 +610,7 @@ std::function item_count_eval( char scope, std::function item_rad_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value agg_val( std::string{ "min" } ); - if( kwargs.count( "aggregate" ) != 0 ) { - agg_val = *kwargs.at( "aggregate" ); - } + diag_value agg_val = kwargs.kwarg_or( "aggregate", "min" ); return [beta = is_beta( scope ), flag = params[0], agg_val]( dialogue const & d ) { std::optional const agg = @@ -745,21 +723,14 @@ bool _filter_character( Character const *beta, Character const &guy, int radius, std::function _characters_nearby_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value radius_val( 1000.0 ); - diag_value filter_val( std::string{ "any" } ); - diag_value allow_hallucinations_val( 0.0 ); + diag_value radius_val = kwargs.kwarg_or( "radius", 1000 ); + diag_value filter_val = kwargs.kwarg_or( "attitude", "any" ); + diag_value allow_hallucinations_val = kwargs.kwarg_or( "allow_hallucinations" ); std::optional loc_var; - if( kwargs.count( "radius" ) != 0 ) { - radius_val = *kwargs.at( "radius" ); - } - if( kwargs.count( "attitude" ) != 0 ) { - filter_val = *kwargs.at( "attitude" ); - } - if( kwargs.count( "allow_hallucinations" ) != 0 ) { - allow_hallucinations_val = *kwargs.at( "allow_hallucinations" ); - } - if( kwargs.count( "location" ) != 0 ) { - loc_var = kwargs.at( "location" )->var(); + diag_value loc_val = kwargs.kwarg_or( "location" ); + + if( !loc_val.is_empty() ) { + loc_var = loc_val.var(); } else if( scope == 'g' ) { throw std::invalid_argument( string_format( R"("characters_nearby" needs either an actor scope (u/n) or a 'location' kwarg)" ) ); @@ -868,17 +839,13 @@ template std::function _monsters_nearby_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs, f_monster_match f ) { - diag_value radius_val( 1000.0 ); - diag_value filter_val( std::string{ "hostile" } ); + diag_value radius_val = kwargs.kwarg_or( "radius", 1000 ); + diag_value filter_val = kwargs.kwarg_or( "attitude", "hostile" ); + diag_value loc_val = kwargs.kwarg_or( "location" ); std::optional loc_var; - if( kwargs.count( "radius" ) != 0 ) { - radius_val = *kwargs.at( "radius" ); - } - if( kwargs.count( "attitude" ) != 0 ) { - filter_val = *kwargs.at( "attitude" ); - } - if( kwargs.count( "location" ) != 0 ) { - loc_var = kwargs.at( "location" )->var(); + + if( !loc_val.is_empty() ) { + loc_var = loc_val.var(); } else if( scope == 'g' ) { throw std::invalid_argument( string_format( R"("monsters_nearby" needs either an actor scope (u/n) or a 'location' kwarg)" ) ); @@ -945,10 +912,8 @@ std::function moon_phase_eval( char /* scope */, std::function pain_eval( char scope, std::vector const &/* params */, diag_kwargs const &kwargs ) { - diag_value format_value( std::string( "raw" ) ); - if( kwargs.count( "type" ) != 0 ) { - format_value = *kwargs.at( "type" ); - } + diag_value format_value = kwargs.kwarg_or( "type", "raw" ); + return [format_value, beta = is_beta( scope )]( dialogue const & d ) { std::string format = format_value.str( d ); if( format == "perceived" ) { @@ -1027,16 +992,8 @@ std::function school_level_adjustment_ass( char scop std::function get_daily_calories( char scope, std::vector const &/* params */, diag_kwargs const &kwargs ) { - diag_value type_val( std::string( "total" ) ); - diag_value day_val( 0.0 ); - - if( kwargs.count( "day" ) != 0 ) { - day_val = *kwargs.at( "day" ); - } - - if( kwargs.count( "type" ) != 0 ) { - type_val = *kwargs.at( "type" ); - } + diag_value type_val = kwargs.kwarg_or( "type", "total" ); + diag_value day_val = kwargs.kwarg_or( "day" ); return[beta = is_beta( scope ), day_val, type_val ]( dialogue const & d ) { std::string type = type_val.str( d ); @@ -1069,10 +1026,7 @@ std::function skill_ass( char scope, std::function skill_exp_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value format_value( std::string( "percentage" ) ); - if( kwargs.count( "format" ) != 0 ) { - format_value = *kwargs.at( "format" ); - } + diag_value format_value = kwargs.kwarg_or( "format", "percentage" ); return[skill_value = params[0], format_value, beta = is_beta( scope )]( dialogue const & d ) { skill_id skill( skill_value.str( d ) ); @@ -1088,10 +1042,7 @@ std::function skill_exp_eval( char scope, std::function skill_exp_ass( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value format_value( std::string( "percentage" ) ); - if( kwargs.count( "format" ) != 0 ) { - format_value = *kwargs.at( "format" ); - } + diag_value format_value = kwargs.kwarg_or( "format", "percentage" ); return [skill_value = params[0], format_value, beta = is_beta( scope ) ]( dialogue const & d, double val ) { @@ -1108,10 +1059,8 @@ std::function skill_exp_ass( char scope, std::function spell_count_eval( char scope, std::vector const &/* params */, diag_kwargs const &kwargs ) { - diag_value school_value( std::string{} ); - if( kwargs.count( "school" ) != 0 ) { - school_value = *kwargs.at( "school" ); - } + diag_value school_value = kwargs.kwarg_or( "school" ); + return[beta = is_beta( scope ), school_value]( dialogue const & d ) { std::string school_str = school_value.str( d ); const trait_id scid = school_str.empty() ? trait_id::NULL_ID() : trait_id( school_str ); @@ -1122,16 +1071,8 @@ std::function spell_count_eval( char scope, std::function spell_sum_eval( char scope, std::vector const &/* params */, diag_kwargs const &kwargs ) { - diag_value school_value( std::string{} ); - diag_value min_level( 0.0 ); - - if( kwargs.count( "school" ) != 0 ) { - school_value = *kwargs.at( "school" ); - } - - if( kwargs.count( "level" ) != 0 ) { - min_level = *kwargs.at( "level" ); - } + diag_value school_value = kwargs.kwarg_or( "school" ); + diag_value min_level = kwargs.kwarg_or( "level" ); return[beta = is_beta( scope ), school_value, min_level]( dialogue const & d ) { std::string school_str = school_value.str( d ); @@ -1256,10 +1197,7 @@ double _time_in_unit( double time, std::string_view unit ) std::function time_eval( char /* scope */, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value unit_val( std::string{} ); - if( kwargs.count( "unit" ) != 0 ) { - unit_val = *kwargs.at( "unit" ); - } + diag_value unit_val = kwargs.kwarg_or( "unit" ); return [val = params[0], unit_val]( dialogue const & d ) { std::string const val_str = val.str( d ); @@ -1294,10 +1232,7 @@ std::function time_ass( char /* scope */, std::function time_since_eval( char /* scope */, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value unit_val( std::string{} ); - if( kwargs.count( "unit" ) != 0 ) { - unit_val = *kwargs.at( "unit" ); - } + diag_value unit_val = kwargs.kwarg_or( "unit" ); return [val = params[0], unit_val]( dialogue const & d ) { double ret{}; @@ -1320,10 +1255,7 @@ std::function time_since_eval( char /* scope */, std::function time_until_eval( char /* scope */, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value unit_val( std::string{} ); - if( kwargs.count( "unit" ) != 0 ) { - unit_val = *kwargs.at( "unit" ); - } + diag_value unit_val = kwargs.kwarg_or( "unit" ); return [val = params[0], unit_val]( dialogue const & d ) { double ret{}; @@ -1356,10 +1288,7 @@ std::function time_until_eval( char /* scope */, std::function time_until_eoc_eval( char /* scope */, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value unit_val( std::string{} ); - if( kwargs.count( "unit" ) != 0 ) { - unit_val = *kwargs.at( "unit" ); - } + diag_value unit_val = kwargs.kwarg_or( "unit" ); return [eoc_val = params[0], unit_val]( dialogue const & d ) -> double { effect_on_condition_id eoc_id( eoc_val.str( d ) ); @@ -1376,15 +1305,8 @@ std::function time_until_eoc_eval( char /* scope */, std::function effect_duration_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value bp_val( std::string{} ); - if( kwargs.count( "bodypart" ) != 0 ) { - bp_val = *kwargs.at( "bodypart" ); - } - - diag_value unit_val( std::string{} ); - if( kwargs.count( "unit" ) != 0 ) { - unit_val = *kwargs.at( "unit" ); - } + diag_value bp_val = kwargs.kwarg_or( "bodypart" ); + diag_value unit_val = kwargs.kwarg_or( "unit" ); return[effect_id = params[0], bp_val, unit_val, beta = is_beta( scope )]( dialogue const & d ) { std::string const bp_str = bp_val.str( d ); @@ -1398,10 +1320,8 @@ std::function effect_duration_eval( char scope, std::function proficiency_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value fmt_val( std::string{"time_spent"} ); - if( kwargs.count( "format" ) != 0 ) { - fmt_val = *kwargs.at( "format" ); - } + diag_value fmt_val = kwargs.kwarg_or( "format", "time_spent" ); + return [beta = is_beta( scope ), prof_value = params[0], fmt_val]( dialogue const & d ) { proficiency_id prof( prof_value.str( d ) ); time_duration raw = d.actor( beta )->proficiency_practiced_time( prof ); @@ -1426,14 +1346,9 @@ std::function proficiency_eval( char scope, std::function proficiency_ass( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { - diag_value fmt_val( std::string{"time_spent"} ); - diag_value direct_val( 0.0 ); - if( kwargs.count( "format" ) != 0 ) { - fmt_val = *kwargs.at( "format" ); - } - if( kwargs.count( "direct" ) != 0 ) { - direct_val = *kwargs.at( "direct" ); - } + diag_value fmt_val = kwargs.kwarg_or( "format", "time_spent" ); + diag_value direct_val = kwargs.kwarg_or( "direct" ); + return [prof_value = params[0], fmt_val, direct_val, beta = is_beta( scope )]( dialogue const & d, double val ) { proficiency_id prof( prof_value.str( d ) ); @@ -1495,7 +1410,7 @@ std::function _test_func( std::vector const &p double ( *f )( diag_value const &v, dialogue const &d ) ) { std::vector all_params( params ); - for( diag_kwargs::value_type const &v : kwargs ) { + for( diag_kwargs::impl_t::value_type const &v : kwargs.kwargs ) { if( v.first != "test_unused_kwarg" ) { all_params.emplace_back( *v.second ); } @@ -1635,16 +1550,9 @@ std::function npc_trust_ass( char scope, std::function calories_eval( char scope, std::vector const &/* params */, diag_kwargs const &kwargs ) { - diag_value format_value( std::string( "raw" ) ); - if( kwargs.count( "format" ) != 0 ) { - format_value = *kwargs.at( "format" ); - } - + diag_value format_value = kwargs.kwarg_or( "format", "raw" ); // dummy kwarg, intentionally discarded! - diag_value ignore_weariness_val( 0.0 ); - if( kwargs.count( "dont_affect_weariness" ) != 0 ) { - ignore_weariness_val = *kwargs.at( "dont_affect_weariness" ); - } + diag_value ignore_weariness_val = kwargs.kwarg_or( "dont_affect_weariness" ); return[format_value, beta = is_beta( scope )]( dialogue const & d ) -> double { std::string format = format_value.str( d ); @@ -1687,10 +1595,8 @@ std::function calories_eval( char scope, std::function calories_ass( char scope, std::vector const &/* params */, diag_kwargs const &kwargs ) { - diag_value ignore_weariness_val( 0.0 ); - if( kwargs.count( "dont_affect_weariness" ) != 0 ) { - ignore_weariness_val = *kwargs.at( "dont_affect_weariness" ); - } + diag_value ignore_weariness_val = kwargs.kwarg_or( "dont_affect_weariness" ); + return[ignore_weariness_val, beta = is_beta( scope ) ]( dialogue const & d, double val ) { const bool ignore_weariness = is_true( ignore_weariness_val.dbl( d ) ); int current_kcal = d.actor( beta )->get_stored_kcal(); diff --git a/src/math_parser_diag.h b/src/math_parser_diag.h index 8796750121af4..00c11db7c7bf4 100644 --- a/src/math_parser_diag.h +++ b/src/math_parser_diag.h @@ -8,9 +8,21 @@ #include #include -struct diag_value; -struct deref_diag_value; -using diag_kwargs = std::map; +#include "math_parser_diag_value.h" + +struct diag_kwargs { + using impl_t = std::map; + + impl_t kwargs; + + template + diag_value kwarg_or( std::string const &key, T const &default_value = {} ) const { + if( auto it = kwargs.find( key ); it != kwargs.end() ) { + return *( it->second ); + } + return diag_value{ default_value }; + } +}; struct dialogue; struct dialogue_func { diff --git a/src/math_parser_diag_value.cpp b/src/math_parser_diag_value.cpp index 68d80cec7d535..13855be80ecd2 100644 --- a/src/math_parser_diag_value.cpp +++ b/src/math_parser_diag_value.cpp @@ -33,6 +33,11 @@ template constexpr R _diag_value_at_parse_time( diag_value::impl_t const &data ) { return std::visit( overloaded{ + []( std::monostate const &/* std */ ) -> R + { + static R null_R{}; + return null_R; + }, []( C const & v ) -> R { return v; @@ -69,6 +74,10 @@ double diag_value::dbl() const double diag_value::dbl( dialogue const &d ) const { return std::visit( overloaded{ + []( std::monostate const &/* std */ ) + { + return 0.0; + }, []( double v ) { return v; @@ -117,6 +126,10 @@ std::string_view diag_value::str() const std::string diag_value::str( dialogue const &d ) const { return std::visit( overloaded{ + []( std::monostate const &/* std */ ) + { + return std::string{}; + }, []( double v ) { // NOLINTNEXTLINE(cata-translate-string-literal) @@ -154,11 +167,21 @@ var_info diag_value::var() const return _diag_value_at_parse_time( data ); } +var_info diag_value::var( dialogue const &/* d */ ) const +{ + return _diag_value_at_parse_time( data ); +} + bool diag_value::is_array() const { return std::holds_alternative( data ); } +bool diag_value::is_empty() const +{ + return std::holds_alternative( data ); +} + diag_array const &diag_value::array() const { return _diag_value_at_parse_time( data ); diff --git a/src/math_parser_diag_value.h b/src/math_parser_diag_value.h index a80b54629f4a9..a14c8af2ab8fa 100644 --- a/src/math_parser_diag_value.h +++ b/src/math_parser_diag_value.h @@ -15,31 +15,64 @@ class math_exp; struct dialogue; struct diag_value; using diag_array = std::vector; + +// https://stackoverflow.com/a/45896101 +// *INDENT-OFF* +template +struct is_one_of; +template +// NOLINTNEXTLINE(cata-avoid-alternative-tokens) broken check +struct is_one_of> : std::bool_constant<( std::is_same_v || ... )> {}; +template +using is_variant_type = is_one_of; +// *INDENT-ON* + struct diag_value { + using impl_t = std::variant; + diag_value() = default; - template + + // *INDENT-OFF* astyle formatting isn't stable here + template {} && std::is_arithmetic_v, int> = 0> + // NOLINTNEXTLINE(google-explicit-constructor) + diag_value( D d ) : data( std::in_place_type, std::forward( d ) ) {} + + template {} && std::is_convertible_v, int> = 0> + // NOLINTNEXTLINE(google-explicit-constructor) + diag_value( S s ) : data( std::in_place_type, std::forward( s ) ) {} + + template {}, int> = 0> explicit diag_value( U u ) : data( std::in_place_type, std::forward( u ) ) {} + // *INDENT-ON* + template explicit diag_value( std::in_place_type_t /*t*/, Args &&...args ) : data( std::in_place_type, std::forward( args )... ) {} - // these functions can be used at parse time if the parameter needs to be of exactly this type - // with no conversion. These throw so they should *NOT* be used at runtime. bool is_dbl() const; - double dbl() const; bool is_str() const; - std::string_view str() const; bool is_var() const; - var_info var() const; bool is_array() const; + bool is_empty() const; + + // these functions can be used at parse time if the parameter needs + // to be of exactly this type with no conversion. + // These throw so they should *NOT* be used at runtime. + double dbl() const; + std::string_view str() const; diag_array const &array() const; + var_info var() const; - // evaluate and possibly convert the parameter to this type + // evaluate and possibly convert the parameter to this type. + // These do not throw and they're meant to be used at runtime double dbl( dialogue const &d ) const; std::string str( dialogue const &d ) const; + var_info var( dialogue const &/* d */ ) const; diag_array const &array( dialogue const &/* d */ ) const; - using impl_t = std::variant; impl_t data; }; diff --git a/src/math_parser_impl.h b/src/math_parser_impl.h index a71f6897def27..8d8cc63b9a44a 100644 --- a/src/math_parser_impl.h +++ b/src/math_parser_impl.h @@ -12,6 +12,7 @@ #include "cata_utility.h" #include "debug.h" #include "dialogue_helpers.h" +#include "type_id.h" #include "math_parser_diag.h" #include "math_parser_func.h"