diff --git a/src/catacharset.cpp b/src/catacharset.cpp index d3018825249dd..89f7b87684e8f 100644 --- a/src/catacharset.cpp +++ b/src/catacharset.cpp @@ -415,21 +415,31 @@ std::u32string utf8_to_utf32( const std::string_view str ) std::vector utf8_display_split( const std::string &s ) { std::vector result; - std::string current_glyph; + std::vector parts; + utf8_display_split_into( s, parts ); + result.reserve( parts.size() ); + for( std::string_view part : parts ) { + result.emplace_back( part ); + } + return result; +} + +void utf8_display_split_into( const std::string &s, std::vector &result ) +{ const char *pos = s.c_str(); + const char *glyph_begin = pos; + const char *glyph_end = pos; int len = s.length(); while( len > 0 ) { - const char *old_pos = pos; const uint32_t ch = UTF8_getch( &pos, &len ); const int width = mk_wcwidth( ch ); - if( width > 0 && !current_glyph.empty() ) { - result.push_back( current_glyph ); - current_glyph.clear(); + if( width > 0 && glyph_begin != glyph_end ) { + result.emplace_back( glyph_begin, std::distance( glyph_begin, glyph_end ) ); + glyph_begin = glyph_end; } - current_glyph += std::string( old_pos, pos ); + glyph_end = pos; } - result.push_back( current_glyph ); - return result; + result.emplace_back( glyph_begin, std::distance( glyph_begin, glyph_end ) ); } int center_text_pos( const char *text, int start_pos, int end_pos ) diff --git a/src/catacharset.h b/src/catacharset.h index c93e3f7401bf3..a12432f930bc2 100644 --- a/src/catacharset.h +++ b/src/catacharset.h @@ -59,6 +59,7 @@ std::u32string utf8_to_utf32( std::string_view str ); // Split the given string into displayed characters. Each element of the returned vector // contains one 'regular' codepoint and all subsequent combining characters. std::vector utf8_display_split( const std::string & ); +void utf8_display_split_into( const std::string &, std::vector & ); /** * UTF8-Wrapper over std::string. diff --git a/src/field.cpp b/src/field.cpp index 6a91a1c22c8cc..675f34b967965 100644 --- a/src/field.cpp +++ b/src/field.cpp @@ -124,8 +124,8 @@ field_entry *field::find_field( const field_type_id &field_type_to_find, const b if( !_displayed_field_type ) { return nullptr; } - const auto it = _field_type_list.find( field_type_to_find ); - if( it != _field_type_list.end() && ( !alive_only || it->second.is_field_alive() ) ) { + const auto it = _field_type_list->find( field_type_to_find ); + if( it != _field_type_list->end() && ( !alive_only || it->second.is_field_alive() ) ) { return &it->second; } return nullptr; @@ -137,8 +137,8 @@ const field_entry *field::find_field( const field_type_id &field_type_to_find, if( !_displayed_field_type ) { return nullptr; } - const auto it = _field_type_list.find( field_type_to_find ); - if( it != _field_type_list.end() && ( !alive_only || it->second.is_field_alive() ) ) { + const auto it = _field_type_list->find( field_type_to_find ); + if( it != _field_type_list->end() && ( !alive_only || it->second.is_field_alive() ) ) { return &it->second; } return nullptr; @@ -159,8 +159,8 @@ bool field::add_field( const field_type_id &field_type_to_add, const int new_int if( !field_type_to_add ) { return false; } - auto it = _field_type_list.find( field_type_to_add ); - if( it != _field_type_list.end() ) { + auto it = _field_type_list->find( field_type_to_add ); + if( it != _field_type_list->end() ) { //Already exists, but lets update it. This is tentative. int prev_intensity = it->second.get_field_intensity(); if( !it->second.is_field_alive() ) { @@ -180,8 +180,8 @@ bool field::add_field( const field_type_id &field_type_to_add, const int new_int bool field::remove_field( const field_type_id &field_to_remove ) { - const auto it = _field_type_list.find( field_to_remove ); - if( it == _field_type_list.end() ) { + const auto it = _field_type_list->find( field_to_remove ); + if( it == _field_type_list->end() ) { return false; } remove_field( it ); @@ -190,9 +190,9 @@ bool field::remove_field( const field_type_id &field_to_remove ) void field::remove_field( std::map::iterator const it ) { - _field_type_list.erase( it ); + _field_type_list->erase( it ); _displayed_field_type = fd_null; - for( auto &fld : _field_type_list ) { + for( auto &fld : *_field_type_list ) { if( !_displayed_field_type || fld.first.obj().priority >= _displayed_field_type.obj().priority ) { _displayed_field_type = fld.first; } @@ -201,7 +201,7 @@ void field::remove_field( std::map::iterator const i void field::clear() { - _field_type_list.clear(); + _field_type_list->clear(); _displayed_field_type = fd_null; } @@ -211,27 +211,27 @@ Returns the number of fields existing on the current tile. */ unsigned int field::field_count() const { - return _field_type_list.size(); + return _field_type_list->size(); } std::map::iterator field::begin() { - return _field_type_list.begin(); + return _field_type_list->begin(); } std::map::const_iterator field::begin() const { - return _field_type_list.begin(); + return _field_type_list->begin(); } std::map::iterator field::end() { - return _field_type_list.end(); + return _field_type_list->end(); } std::map::const_iterator field::end() const { - return _field_type_list.end(); + return _field_type_list->end(); } /* @@ -250,14 +250,14 @@ description_affix field::displayed_description_affix() const int field::displayed_intensity() const { - auto it = _field_type_list.find( _displayed_field_type ); + auto it = _field_type_list->find( _displayed_field_type ); return it->second.get_field_intensity(); } int field::total_move_cost() const { int current_cost = 0; - for( const auto &fld : _field_type_list ) { + for( const auto &fld : *_field_type_list ) { current_cost += fld.second.get_intensity_level().move_cost; } return current_cost; diff --git a/src/field.h b/src/field.h index 0988758a1375c..8225ae3384b9e 100644 --- a/src/field.h +++ b/src/field.h @@ -7,6 +7,7 @@ #include #include "calendar.h" +#include "cata_lazy.h" #include "color.h" #include "enums.h" #include "field_type.h" @@ -180,7 +181,7 @@ class field private: // A pointer lookup table of all field effects on the current tile. - std::map _field_type_list; + lazy> _field_type_list; //_displayed_field_type currently is equal to the last field added to the square. You can modify this behavior in the class functions if you wish. field_type_id _displayed_field_type; }; diff --git a/src/item.cpp b/src/item.cpp index 805b560e096f7..6fc86ab1da1a9 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -415,7 +415,7 @@ item::item( const itype_id &id, time_point turn, solitary_tag tag ) safe_reference item::get_safe_reference() { - return anchor.reference_to( this ); + return anchor->reference_to( this ); } static const item *get_most_rotten_component( const item &craft ) @@ -587,10 +587,10 @@ item::item( const recipe *rec, int qty, item &component ) } item::item( const item & ) = default; -item::item( item && ) noexcept( map_is_noexcept ) = default; +item::item( item && ) noexcept = default; item::~item() = default; item &item::operator=( const item & ) = default; -item &item::operator=( item && ) noexcept( list_is_noexcept ) = default; +item &item::operator=( item && ) noexcept = default; item item::make_corpse( const mtype_id &mt, time_point turn, const std::string &name, const int upgrade_time ) @@ -1489,7 +1489,8 @@ bool item::stacks_with( const item &rhs, bool check_components, bool combine_liq // Guns that differ only by dirt/shot_counter can still stack, // but other item_vars such as label/note will prevent stacking const std::vector ignore_keys = { "dirt", "shot_counter", "spawn_location_omt" }; - if( map_without_keys( item_vars, ignore_keys ) != map_without_keys( rhs.item_vars, ignore_keys ) ) { + if( map_without_keys( *item_vars, ignore_keys ) != map_without_keys( *rhs.item_vars, + ignore_keys ) ) { return false; } const std::string omt_loc_var = "spawn_location_omt"; diff --git a/src/item.h b/src/item.h index 859cf5bae5f3d..6431c96516eb5 100644 --- a/src/item.h +++ b/src/item.h @@ -17,6 +17,7 @@ #include #include "calendar.h" +#include "cata_lazy.h" #include "cata_utility.h" #include "compatibility.h" #include "enums.h" @@ -192,9 +193,9 @@ class item : public visitable item(); - item( item && ) noexcept( map_is_noexcept ); + item( item && ) noexcept; item( const item & ); - item &operator=( item && ) noexcept( list_is_noexcept ); + item &operator=( item && ) noexcept; item &operator=( const item & ); explicit item( const itype_id &id, time_point turn = calendar::turn, int qty = -1 ); @@ -2995,7 +2996,7 @@ class item : public visitable const itype *type; item_components components; /** What faults (if any) currently apply to this item */ - std::set faults; + cata::heap> faults; private: item_contents contents; @@ -3003,13 +3004,13 @@ class item : public visitable * This flag is reset to `true` if item tags are changed. */ bool requires_tags_processing = true; - FlagsSetType item_tags; // generic item specific flags - FlagsSetType inherited_tags_cache; - safe_reference_anchor anchor; - std::map item_vars; + cata::heap item_tags; // generic item specific flags + cata::heap inherited_tags_cache; + lazy anchor; + cata::heap> item_vars; const mtype *corpse = nullptr; std::string corpse_name; // Name of the late lamented - std::set techniques; // item specific techniques + cata::heap> techniques; // item specific techniques // Select a random variant from the possibilities // Intended to be called when no explicit variant is set diff --git a/src/item_components.h b/src/item_components.h index da59cacb87b4f..b8e3c05adf4c8 100644 --- a/src/item_components.h +++ b/src/item_components.h @@ -8,6 +8,7 @@ #include #include "type_id.h" +#include "value_ptr.h" class item; class JsonOut; @@ -18,7 +19,7 @@ class ret_val; class item_components { private: - std::map> comps; + cata::heap>> comps; using comp_iterator = std::map>::iterator; using const_comp_iterator = std::map>::const_iterator; diff --git a/src/item_group.cpp b/src/item_group.cpp index 9c1fc0931eefd..ed705366b713d 100644 --- a/src/item_group.cpp +++ b/src/item_group.cpp @@ -156,9 +156,11 @@ static void put_into_container( ctr.seal(); } - excess.emplace_back( std::move( ctr ) ); items.erase( items.end() - num_items, items.end() ); - items.insert( items.end(), excess.begin(), excess.end() ); + items.reserve( items.size() + excess.size() + 1 ); + items.insert( items.end(), std::make_move_iterator( excess.begin() ), + std::make_move_iterator( excess.end() ) ); + items.emplace_back( std::move( ctr ) ); } Single_item_creator::Single_item_creator( const std::string &_id, Type _type, int _probability, @@ -497,151 +499,153 @@ void Item_modifier::modify( item &new_item, const std::string &context ) const new_item.set_itype_variant( variant ); - // create container here from modifier or from default to get max charges later - item cont; - if( container != nullptr ) { - cont = container->create_single( new_item.birthday() ); - } else if( new_item.type->default_container.has_value() ) { - cont = item( *new_item.type->default_container, new_item.birthday() ); - } + { + // create container here from modifier or from default to get max charges later + item cont; + if( container != nullptr ) { + cont = container->create_single( new_item.birthday() ); + } else if( new_item.type->default_container.has_value() ) { + cont = item( *new_item.type->default_container, new_item.birthday() ); + } - int max_capacity = -1; + int max_capacity = -1; - if( charges.first != -1 && charges.second == -1 && ( new_item.is_magazine() || - new_item.uses_magazine() ) ) { - int max_ammo = 0; + if( charges.first != -1 && charges.second == -1 && ( new_item.is_magazine() || + new_item.uses_magazine() ) ) { + int max_ammo = 0; - if( new_item.is_magazine() ) { - // Get the ammo capacity of the new item itself - max_ammo = new_item.ammo_capacity( item_controller->find_template( - new_item.ammo_default() )->ammo->type ); - } else if( !new_item.magazine_default().is_null() ) { - // Get the capacity of the item's default magazine - max_ammo = item_controller->find_template( new_item.magazine_default() )->magazine->capacity; - } - // Don't change the ammo capacity from 0 if the item isn't a magazine - // and doesn't have a default magazine with a capacity + if( new_item.is_magazine() ) { + // Get the ammo capacity of the new item itself + max_ammo = new_item.ammo_capacity( item_controller->find_template( + new_item.ammo_default() )->ammo->type ); + } else if( !new_item.magazine_default().is_null() ) { + // Get the capacity of the item's default magazine + max_ammo = item_controller->find_template( new_item.magazine_default() )->magazine->capacity; + } + // Don't change the ammo capacity from 0 if the item isn't a magazine + // and doesn't have a default magazine with a capacity - if( max_ammo > 0 ) { - max_capacity = max_ammo; + if( max_ammo > 0 ) { + max_capacity = max_ammo; + } } - } - if( max_capacity == -1 && !cont.is_null() && ( new_item.made_of( phase_id::LIQUID ) || - ( !new_item.is_tool() && !new_item.is_gun() && !new_item.is_magazine() ) ) ) { - if( new_item.type->weight == 0_gram ) { - max_capacity = new_item.charges_per_volume( cont.get_total_capacity() ); - } else { - max_capacity = std::min( new_item.charges_per_volume( cont.get_total_capacity() ), - new_item.charges_per_weight( cont.get_total_weight_capacity() ) ); + if( max_capacity == -1 && !cont.is_null() && ( new_item.made_of( phase_id::LIQUID ) || + ( !new_item.is_tool() && !new_item.is_gun() && !new_item.is_magazine() ) ) ) { + if( new_item.type->weight == 0_gram ) { + max_capacity = new_item.charges_per_volume( cont.get_total_capacity() ); + } else { + max_capacity = std::min( new_item.charges_per_volume( cont.get_total_capacity() ), + new_item.charges_per_weight( cont.get_total_weight_capacity() ) ); + } } - } - const bool charges_not_set = charges.first == -1 && charges.second == -1; - int ch = -1; - if( !charges_not_set ) { - int charges_min = charges.first == -1 ? 0 : charges.first; - int charges_max = charges.second == -1 ? max_capacity : charges.second; + const bool charges_not_set = charges.first == -1 && charges.second == -1; + int ch = -1; + if( !charges_not_set ) { + int charges_min = charges.first == -1 ? 0 : charges.first; + int charges_max = charges.second == -1 ? max_capacity : charges.second; - if( charges_min == -1 && charges_max != -1 ) { - charges_min = 0; - } + if( charges_min == -1 && charges_max != -1 ) { + charges_min = 0; + } - if( max_capacity != -1 && ( charges_max > max_capacity || ( charges_min != 1 && - charges_max == -1 ) ) ) { - charges_max = max_capacity; - } + if( max_capacity != -1 && ( charges_max > max_capacity || ( charges_min != 1 && + charges_max == -1 ) ) ) { + charges_max = max_capacity; + } + + if( charges_min > charges_max ) { + charges_min = charges_max; + } - if( charges_min > charges_max ) { - charges_min = charges_max; + ch = charges_min == charges_max ? charges_min : rng( charges_min, + charges_max ); + } else if( !cont.is_null() && new_item.made_of( phase_id::LIQUID ) ) { + new_item.charges = std::max( 1, max_capacity ); } - ch = charges_min == charges_max ? charges_min : rng( charges_min, - charges_max ); - } else if( !cont.is_null() && new_item.made_of( phase_id::LIQUID ) ) { - new_item.charges = std::max( 1, max_capacity ); - } - - if( ch != -1 ) { - if( new_item.count_by_charges() || new_item.made_of( phase_id::LIQUID ) ) { - // food, ammo - // count_by_charges requires that charges is at least 1. It makes no sense to - // spawn a "water (0)" item. - new_item.charges = std::max( 1, ch ); - } else if( new_item.is_tool() ) { - if( !new_item.magazine_current() && !new_item.magazine_default().is_null() ) { - item mag( new_item.magazine_default() ); - if( !mag.ammo_default().is_null() ) { - mag.ammo_set( mag.ammo_default(), ch ); + if( ch != -1 ) { + if( new_item.count_by_charges() || new_item.made_of( phase_id::LIQUID ) ) { + // food, ammo + // count_by_charges requires that charges is at least 1. It makes no sense to + // spawn a "water (0)" item. + new_item.charges = std::max( 1, ch ); + } else if( new_item.is_tool() ) { + if( !new_item.magazine_current() && !new_item.magazine_default().is_null() ) { + item mag( new_item.magazine_default() ); + if( !mag.ammo_default().is_null() ) { + mag.ammo_set( mag.ammo_default(), ch ); + } + new_item.put_in( mag, pocket_type::MAGAZINE_WELL ); + } else if( new_item.is_magazine() ) { + new_item.ammo_set( new_item.ammo_default(), ch ); + } else if( new_item.magazine_current() ) { + new_item.ammo_set( new_item.magazine_current()->ammo_default(), ch ); + } else { + debugmsg( "in %s: tried to set ammo for %s which does not have ammo or a magazine", + context, new_item.typeId().str() ); } - new_item.put_in( mag, pocket_type::MAGAZINE_WELL ); - } else if( new_item.is_magazine() ) { - new_item.ammo_set( new_item.ammo_default(), ch ); - } else if( new_item.magazine_current() ) { - new_item.ammo_set( new_item.magazine_current()->ammo_default(), ch ); - } else { - debugmsg( "in %s: tried to set ammo for %s which does not have ammo or a magazine", - context, new_item.typeId().str() ); + } else if( new_item.type->can_have_charges() ) { + new_item.charges = ch; } - } else if( new_item.type->can_have_charges() ) { - new_item.charges = ch; - } - } - - if( ch > 0 && ( new_item.is_gun() || new_item.is_magazine() ) ) { - itype_id ammo_id; - if( ammo ) { - ammo_id = ammo->create_single( new_item.birthday() ).typeId(); - } else if( new_item.ammo_default() ) { - ammo_id = new_item.ammo_default(); - } else if( new_item.magazine_default() && new_item.magazine_default()->magazine->default_ammo ) { - ammo_id = new_item.magazine_default()->magazine->default_ammo; - } - if( ammo_id && !ammo_id.is_empty() ) { - new_item.ammo_set( ammo_id, ch ); - } else { - debugmsg( "tried to set ammo for %s which does not have ammo or a magazine", - new_item.typeId().c_str() ); } - // Make sure the item is in valid state - if( new_item.magazine_integral() ) { - new_item.charges = std::min( new_item.charges, - new_item.ammo_capacity( item_controller->find_template( new_item.ammo_default() )->ammo->type ) ); - } else { - new_item.charges = 0; - } - } - if( new_item.is_magazine() || - new_item.has_pocket_type( pocket_type::MAGAZINE_WELL ) ) { - bool spawn_ammo = rng( 0, 99 ) < with_ammo && new_item.ammo_remaining() == 0 && ch == -1 && - ( !new_item.is_tool() || new_item.type->tool->rand_charges.empty() ); - bool spawn_mag = rng( 0, 99 ) < with_magazine && !new_item.magazine_integral() && - !new_item.magazine_current(); - - if( spawn_mag ) { - item mag( new_item.magazine_default(), new_item.birthday() ); - if( spawn_ammo && !mag.ammo_default().is_null() ) { - mag.ammo_set( mag.ammo_default() ); - } - new_item.put_in( mag, pocket_type::MAGAZINE_WELL ); - } else if( spawn_ammo && !new_item.ammo_default().is_null() ) { + if( ch > 0 && ( new_item.is_gun() || new_item.is_magazine() ) ) { + itype_id ammo_id; if( ammo ) { - const item am = ammo->create_single( new_item.birthday() ); - new_item.ammo_set( am.typeId() ); + ammo_id = ammo->create_single( new_item.birthday() ).typeId(); + } else if( new_item.ammo_default() ) { + ammo_id = new_item.ammo_default(); + } else if( new_item.magazine_default() && new_item.magazine_default()->magazine->default_ammo ) { + ammo_id = new_item.magazine_default()->magazine->default_ammo; + } + if( ammo_id && !ammo_id.is_empty() ) { + new_item.ammo_set( ammo_id, ch ); + } else { + debugmsg( "tried to set ammo for %s which does not have ammo or a magazine", + new_item.typeId().c_str() ); + } + // Make sure the item is in valid state + if( new_item.magazine_integral() ) { + new_item.charges = std::min( new_item.charges, + new_item.ammo_capacity( item_controller->find_template( new_item.ammo_default() )->ammo->type ) ); } else { - new_item.ammo_set( new_item.ammo_default() ); + new_item.charges = 0; } } - } - if( !cont.is_null() ) { - const pocket_type pk_type = guess_pocket_for( cont, new_item ); - cont.put_in( new_item, pk_type ); - cont.add_automatic_whitelist(); - new_item = cont; - if( sealed ) { - new_item.seal(); + if( new_item.is_magazine() || + new_item.has_pocket_type( pocket_type::MAGAZINE_WELL ) ) { + bool spawn_ammo = rng( 0, 99 ) < with_ammo && new_item.ammo_remaining() == 0 && ch == -1 && + ( !new_item.is_tool() || new_item.type->tool->rand_charges.empty() ); + bool spawn_mag = rng( 0, 99 ) < with_magazine && !new_item.magazine_integral() && + !new_item.magazine_current(); + + if( spawn_mag ) { + item mag( new_item.magazine_default(), new_item.birthday() ); + if( spawn_ammo && !mag.ammo_default().is_null() ) { + mag.ammo_set( mag.ammo_default() ); + } + new_item.put_in( mag, pocket_type::MAGAZINE_WELL ); + } else if( spawn_ammo && !new_item.ammo_default().is_null() ) { + if( ammo ) { + const item am = ammo->create_single( new_item.birthday() ); + new_item.ammo_set( am.typeId() ); + } else { + new_item.ammo_set( new_item.ammo_default() ); + } + } + } + + if( !cont.is_null() ) { + const pocket_type pk_type = guess_pocket_for( cont, new_item ); + cont.put_in( new_item, pk_type ); + cont.add_automatic_whitelist(); + new_item = std::move( cont ); + if( sealed ) { + new_item.seal(); + } } } diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 442ec509449f3..ed8b070fe1915 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -4491,10 +4491,10 @@ bool mapgen_function_json_base::setup_common( const JsonObject &jo ) } for( int c = m_offset.y; c < expected_dim.y; c++ ) { const std::string row = parray.get_string( c ); - std::vector row_keys; - for( const std::string &key : utf8_display_split( row ) ) { - row_keys.emplace_back( key ); - } + static std::vector row_keys; + row_keys.clear(); + row_keys.reserve( total_size.x ); + utf8_display_split_into( row, row_keys ); if( row_keys.size() < static_cast( expected_dim.x ) ) { parray.throw_error( string_format( " format: row %d must have at least %d columns, not %d", @@ -4507,7 +4507,7 @@ bool mapgen_function_json_base::setup_common( const JsonObject &jo ) } for( int i = m_offset.x; i < expected_dim.x; i++ ) { const point p = point( i, c ) - m_offset; - const map_key key = row_keys[i]; + const map_key key{ std::string( row_keys[i] ) }; const auto iter_ter = keys_with_terrain.find( key ); const auto fpi = format_placings.find( key ); diff --git a/src/value_ptr.h b/src/value_ptr.h index 43a6c694a7030..ab5c04cf7ccd9 100644 --- a/src/value_ptr.h +++ b/src/value_ptr.h @@ -61,6 +61,164 @@ bool value_ptr_equals( const value_ptr &lhs, const value_ptr &rhs ) return ( !lhs && !rhs ) || ( lhs && rhs && *lhs == *rhs ); } +/** + * This class is essentially a copyable unique pointer, like value_ptr, except + * it hides the fact it is a unique_ptr. It is intended for helping make types + * noexcept movable by moving non-noexcept-movable types to the heap. + * + * Contractually speaking, should never be empty. However for performance reasons + * in eg. item, moved-from heap<> objects do not automatically reset with a new + * copy of the wrapped data. + */ +template +struct heap { + private: + std::unique_ptr heaped_; + + public: + template + // NOLINTNEXTLINE(google-explicit-constructor) + heap( Args &&...args ) : heaped_{ new T{ std::forward( args )... } } {} + + // Unlike value_ptr, moves actually move and leave the moved-from heap empty. + // Using it again will cause a segfault without resetting it first. + heap( heap && ) noexcept = default; + heap &operator=( heap &&other ) noexcept = default; + + // Like value_ptr, copies copy the rhs and leave it intact. + heap( heap const &rhs ) { + *this = rhs; + } + heap &operator=( heap const &rhs ) { + // This should always be true, however, sanity says do the right thing. + if( rhs.heaped_ ) { + heaped_.reset( new T{ *rhs.heaped_ } ); + } else { + heaped_.reset( new T{} ); + } + return *this; + } + + heap &operator=( T const &t ) { + heaped_.reset( new T{ t } ); + return *this; + } + + heap &operator=( T &&t ) { + heaped_.reset( new T{ std::move( t ) } ); + return *this; + } + + // Implicit conversion functions + // NOLINTNEXTLINE(google-explicit-constructor) + operator T &() & { // *NOPAD* + return *heaped_; + } + // NOLINTNEXTLINE(google-explicit-constructor) + operator T const &() const & { // *NOPAD* + return *heaped_; + } + // Intentionally move construct a value T to avoid binding a ref to a temporary. + // NOLINTNEXTLINE(google-explicit-constructor) + operator T() && { // *NOPAD* + return std::move( *heaped_ ); + } + + // The one weird one: since this is ultimately backed on the heap, + // allow operator* to get a reference to the thing. + // We don't allow exposing it as a pointer directly, but some places need + // to peel back the heap<> wrapper because otherwise template type deduction + // might break. + T &operator*() & { // *NOPAD* + return *heaped_; + } + T const &operator*() const & { // *NOPAD* + return *heaped_; + } + // Intentionally move construct a value T to avoid binding a ref to a temporary. + T operator*() && { // *NOPAD* + return std::move( *heaped_ ); + } + + private: + // Helper for proxy functions. + T &val() { + return *heaped_; + } + T const &val() const { + return *heaped_; + } + public: + + // Various conditionally defined proxy functions for common types like containers. +#pragma push_macro("PROXY") +#define PROXY(func) \ + template \ + auto func( Us&& ...us ) -> decltype( val().func( std::forward( us )... ) ) { \ + return val().func( std::forward( us )... ); \ + } +#pragma push_macro("PROXY_CONST") +#define PROXY_CONST(func) \ + template \ + auto func( Us&& ...us ) const -> decltype( val().func( std::forward( us )... ) ) { \ + return val().func( std::forward( us )... ); \ + } + + // Comparison operators. + auto operator==( heap const &rhs ) const -> decltype( val() == val() ) { + const T *lhsp = heaped_.get(); + const T *rhsp = rhs.heaped_.get(); + + return rhsp && lhsp && *rhsp == *lhsp; + } + + auto operator!=( heap const &rhs ) const -> decltype( val() != val() ) { + const T *lhsp = heaped_.get(); + const T *rhsp = rhs.heaped_.get(); + + return rhsp && lhsp && *rhsp != *lhsp; + } + + + // Tests & sets + PROXY_CONST( empty ) + PROXY_CONST( count ) + PROXY_CONST( size ) + PROXY( clear ) + + // Iterators + PROXY( begin ) + PROXY_CONST( begin ) + PROXY( end ) + PROXY_CONST( end ) + + // Accessors + template + auto operator[]( U &&u ) -> decltype( val()[std::forward( u )] ) { + return val()[std::forward( u )]; + } + + PROXY( find ) + PROXY_CONST( find ) + + PROXY( erase ) + PROXY( insert ) + PROXY( emplace ) + + // Json* support. + template + void serialize( Stream &jsout ) const { + jsout.write( val() ); + } + + template, JsonValue>>* = nullptr> + void deserialize( const Value &jsin ) { + jsin.read( val() ); + } +#pragma pop_macro("PROXY") +#pragma pop_macro("PROXY_CONST") +}; + } // namespace cata #endif // CATA_SRC_VALUE_PTR_H