diff --git a/src/openvic-simulation/country/CountryDefinition.cpp b/src/openvic-simulation/country/CountryDefinition.cpp index 7c39e6148..cc7111d42 100644 --- a/src/openvic-simulation/country/CountryDefinition.cpp +++ b/src/openvic-simulation/country/CountryDefinition.cpp @@ -67,7 +67,7 @@ bool CountryDefinitionManager::add_country( return country_definitions.emplace_item( identifier, // - identifier, colour, CountryDefinition::index_t { get_country_definition_count() }, *graphical_culture, + identifier, colour, index_from_count(get_country_definition_count()), *graphical_culture, std::move(parties), std::move(unit_names), dynamic_tag, std::move(alternative_colours), /* Default to country colour for the chest and grey for the others. Update later if necessary. */ colour, default_colour, default_colour diff --git a/src/openvic-simulation/economy/BuildingType.cpp b/src/openvic-simulation/economy/BuildingType.cpp index a726b4dcd..6cf61bbf4 100644 --- a/src/openvic-simulation/economy/BuildingType.cpp +++ b/src/openvic-simulation/economy/BuildingType.cpp @@ -129,7 +129,8 @@ bool BuildingTypeManager::add_building_type( const bool ret = building_types.emplace_item( identifier, - BuildingType::index_t { get_building_type_count() }, province_building_index, identifier, building_type_args + index_from_count(get_building_type_count()), province_building_index, identifier, + building_type_args ); if (ret) { diff --git a/src/openvic-simulation/economy/GoodDefinition.cpp b/src/openvic-simulation/economy/GoodDefinition.cpp index fdb8c6f34..14e7ab2d2 100644 --- a/src/openvic-simulation/economy/GoodDefinition.cpp +++ b/src/openvic-simulation/economy/GoodDefinition.cpp @@ -7,7 +7,8 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -GoodCategory::GoodCategory(std::string_view new_identifier) : HasIdentifier { new_identifier } {} +GoodCategory::GoodCategory(std::string_view new_identifier, index_t new_index) + : HasIdentifier { new_identifier }, HasIndex { new_index } {} GoodDefinition::GoodDefinition( std::string_view new_identifier, @@ -34,7 +35,10 @@ bool GoodDefinitionManager::add_good_category(std::string_view identifier, size_ return false; } - if (good_categories.emplace_item(identifier, identifier)) { + if (good_categories.emplace_item( + identifier, + identifier, index_from_count(get_good_category_count()) + )) { good_categories.back().good_definitions.reserve(expected_goods_in_category); return true; } else { @@ -64,7 +68,7 @@ bool GoodDefinitionManager::add_good_definition( if (good_definitions.emplace_item( identifier, - identifier, colour, GoodDefinition::index_t { get_good_definition_count() }, category, base_price, is_available_from_start, + identifier, colour, index_from_count(get_good_definition_count()), category, base_price, is_available_from_start, is_tradeable, is_money, has_overseas_penalty )) { category.good_definitions.emplace_back(get_back_good_definition()); diff --git a/src/openvic-simulation/economy/GoodDefinition.hpp b/src/openvic-simulation/economy/GoodDefinition.hpp index 44a19d2e5..80d48b0a8 100644 --- a/src/openvic-simulation/economy/GoodDefinition.hpp +++ b/src/openvic-simulation/economy/GoodDefinition.hpp @@ -12,13 +12,13 @@ namespace OpenVic { struct GoodDefinitionManager; struct GoodDefinition; - struct GoodCategory : HasIdentifier { + struct GoodCategory : HasIdentifier, HasIndex { friend struct GoodDefinitionManager; private: memory::vector> SPAN_PROPERTY(good_definitions); public: - GoodCategory(std::string_view new_identifier); + GoodCategory(std::string_view new_identifier, index_t new_index); GoodCategory(GoodCategory&&) = default; }; diff --git a/src/openvic-simulation/history/Bookmark.cpp b/src/openvic-simulation/history/Bookmark.cpp index d2fb7c6bc..79efd7a62 100644 --- a/src/openvic-simulation/history/Bookmark.cpp +++ b/src/openvic-simulation/history/Bookmark.cpp @@ -30,7 +30,7 @@ bool BookmarkManager::add_bookmark( ) { return bookmarks.emplace_item( name, - Bookmark::index_t { bookmarks.size() }, name, description, date, initial_camera_position + index_from_count(bookmarks.size()), name, description, date, initial_camera_position ); } diff --git a/src/openvic-simulation/map/Crime.cpp b/src/openvic-simulation/map/Crime.cpp index 45375d9ff..162b78050 100644 --- a/src/openvic-simulation/map/Crime.cpp +++ b/src/openvic-simulation/map/Crime.cpp @@ -29,7 +29,8 @@ bool CrimeManager::add_crime_modifier( return crime_modifiers.emplace_item( identifier, duplicate_warning_callback, - Crime::index_t { get_crime_modifier_count() }, identifier, std::move(values), icon, std::move(trigger), default_active + index_from_count(get_crime_modifier_count()), identifier, std::move(values), icon, + std::move(trigger), default_active ); } diff --git a/src/openvic-simulation/map/Mapmode.cpp b/src/openvic-simulation/map/Mapmode.cpp index 7b4d75767..f90f9a89c 100644 --- a/src/openvic-simulation/map/Mapmode.cpp +++ b/src/openvic-simulation/map/Mapmode.cpp @@ -32,7 +32,7 @@ Mapmode::Mapmode( is_parchment_mapmode_allowed { new_is_parchment_mapmode_allowed } {} const Mapmode Mapmode::ERROR_MAPMODE { - "mapmode_error", index_t { std::numeric_limits::max() }, []( + "mapmode_error", Mapmode::ERROR_INDEX, []( MapInstance const& map_instance, ProvinceInstance const& province, CountryInstance const* player_country, ProvinceInstance const* selected_province ) -> base_stripe_t { @@ -63,7 +63,8 @@ bool MapmodeManager::add_mapmode( } return mapmodes.emplace_item( identifier, - identifier, Mapmode::index_t { get_mapmode_count() }, colour_func, localisation_key, parchment_mapmode_allowed + identifier, index_from_count(get_mapmode_count()), colour_func, localisation_key, + parchment_mapmode_allowed ); } diff --git a/src/openvic-simulation/map/Mapmode.hpp b/src/openvic-simulation/map/Mapmode.hpp index ec00028f9..f6889956a 100644 --- a/src/openvic-simulation/map/Mapmode.hpp +++ b/src/openvic-simulation/map/Mapmode.hpp @@ -1,7 +1,11 @@ #pragma once +#include + #include +#include + #include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/HasIndex.hpp" @@ -36,6 +40,9 @@ namespace OpenVic { memory::string PROPERTY(localisation_key); public: + static constexpr index_t ERROR_INDEX { + std::numeric_limits>::max() + }; static const Mapmode ERROR_MAPMODE; const bool is_parchment_mapmode_allowed; diff --git a/src/openvic-simulation/map/TerrainType.cpp b/src/openvic-simulation/map/TerrainType.cpp index 5153a9df9..2d7a7a5e3 100644 --- a/src/openvic-simulation/map/TerrainType.cpp +++ b/src/openvic-simulation/map/TerrainType.cpp @@ -100,7 +100,7 @@ bool TerrainTypeManager::add_terrain_type( return terrain_types.emplace_item( identifier, - TerrainType::index_t { get_terrain_type_count() }, identifier, + index_from_count(get_terrain_type_count()), identifier, colour, std::move(values), movement_cost, defence_bonus, combat_width_percentage_change, is_water ); } diff --git a/src/openvic-simulation/military/UnitType.cpp b/src/openvic-simulation/military/UnitType.cpp index 924d3dfdf..bf11abf1b 100644 --- a/src/openvic-simulation/military/UnitType.cpp +++ b/src/openvic-simulation/military/UnitType.cpp @@ -16,8 +16,10 @@ using enum unit_branch_t; using enum UnitType::unit_category_t; UnitType::UnitType( - std::string_view new_identifier, unit_branch_t new_branch, unit_type_args_t& unit_args + std::string_view new_identifier, unit_type_index_t new_unit_type_index, unit_branch_t new_branch, + unit_type_args_t& unit_args ) : HasIdentifier { new_identifier }, + unit_type_index { new_unit_type_index }, branch { new_branch }, icon { unit_args.icon }, sprite { unit_args.sprite }, @@ -47,9 +49,9 @@ UnitType::UnitType( } UnitTypeBranched::UnitTypeBranched( - index_t new_index, std::string_view new_identifier, + index_t new_index, unit_type_index_t new_unit_type_index, std::string_view new_identifier, unit_type_args_t& unit_args, regiment_type_args_t const& regiment_type_args -) : UnitType { new_identifier, LAND, unit_args }, +) : UnitType { new_identifier, new_unit_type_index, LAND, unit_args }, HasIndex { new_index }, allowed_cultures { regiment_type_args.allowed_cultures }, sprite_override { regiment_type_args.sprite_override }, @@ -64,9 +66,9 @@ UnitTypeBranched::UnitTypeBranched( siege { regiment_type_args.siege } {} UnitTypeBranched::UnitTypeBranched( - index_t new_index, std::string_view new_identifier, + index_t new_index, unit_type_index_t new_unit_type_index, std::string_view new_identifier, unit_type_args_t& unit_args, ship_type_args_t const& ship_type_args -) : UnitType { new_identifier, NAVAL, unit_args }, +) : UnitType { new_identifier, new_unit_type_index, NAVAL, unit_args }, HasIndex { new_index }, naval_icon { ship_type_args.naval_icon }, can_sail { ship_type_args.sail }, @@ -135,7 +137,8 @@ bool UnitTypeManager::add_regiment_type( bool ret = regiment_types.emplace_item( identifier, - RegimentType::index_t { get_regiment_type_count() }, identifier, + index_from_count(get_regiment_type_count()), + index_from_count(get_unit_type_count()), identifier, unit_args, std::move(regiment_type_args) ); if (ret) { @@ -168,7 +171,8 @@ bool UnitTypeManager::add_ship_type( bool ret = ship_types.emplace_item( identifier, - ShipType::index_t { get_ship_type_count() }, identifier, + index_from_count(get_ship_type_count()), + index_from_count(get_unit_type_count()), identifier, unit_args, ship_type_args ); if (ret) { diff --git a/src/openvic-simulation/military/UnitType.hpp b/src/openvic-simulation/military/UnitType.hpp index 65dda9dca..7f215641a 100644 --- a/src/openvic-simulation/military/UnitType.hpp +++ b/src/openvic-simulation/military/UnitType.hpp @@ -61,9 +61,13 @@ namespace OpenVic { protected: /* Non-const reference unit_args so variables can be moved from it. */ - UnitType(std::string_view new_identifier, unit_branch_t new_branch, unit_type_args_t& unit_args); + UnitType( + std::string_view new_identifier, unit_type_index_t new_unit_type_index, unit_branch_t new_branch, + unit_type_args_t& unit_args + ); public: + const unit_type_index_t unit_type_index; // Position in the combined unit type registry const unit_branch_t branch; /* type in defines */ const unit_category_t unit_category; const bool starts_unlocked; @@ -116,7 +120,7 @@ namespace OpenVic { const fixed_point_t siege; UnitTypeBranched( - index_t new_index, std::string_view new_identifier, + index_t new_index, unit_type_index_t new_unit_type_index, std::string_view new_identifier, unit_type_args_t& unit_args, regiment_type_args_t const& regiment_type_args ); UnitTypeBranched(UnitTypeBranched&&) = default; @@ -155,7 +159,7 @@ namespace OpenVic { const fixed_point_t torpedo_attack; UnitTypeBranched( - index_t new_index, std::string_view new_identifier, + index_t new_index, unit_type_index_t new_unit_type_index, std::string_view new_identifier, unit_type_args_t& unit_args, ship_type_args_t const& ship_type_args ); UnitTypeBranched(UnitTypeBranched&&) = default; @@ -193,6 +197,27 @@ namespace OpenVic { return NodeTools::expect_identifier(expect_branch_str(callback)); } + constexpr NodeTools::NodeCallback auto expect_unit_type_index( + NodeTools::Callback auto callback, bool warn = false + ) const { + return expect_unit_type_identifier( + [callback](UnitType const& unit_type) -> bool { + return callback(unit_type.unit_type_index); + }, + warn + ); + } + constexpr NodeTools::Callback auto expect_unit_type_index_str( + NodeTools::Callback auto callback, bool allow_empty = false, bool warn = false + ) const { + return expect_unit_type_str( + [callback](UnitType const& unit_type) -> bool { + return callback(unit_type.unit_type_index); + }, + allow_empty, warn + ); + } + bool load_unit_type_file( GoodDefinitionManager const& good_definition_manager, TerrainTypeManager const& terrain_type_manager, ModifierManager const& modifier_manager, ovdl::v2script::Parser const& parser diff --git a/src/openvic-simulation/politics/Government.cpp b/src/openvic-simulation/politics/Government.cpp index 5dadda8bd..fc252fb45 100644 --- a/src/openvic-simulation/politics/Government.cpp +++ b/src/openvic-simulation/politics/Government.cpp @@ -52,7 +52,7 @@ bool GovernmentTypeManager::add_government_type( const bool ret = government_types.emplace_item( identifier, - GovernmentType::index_t { get_government_type_count() }, identifier, std::move(ideologies), elections, appoint_ruling_party, term_duration, flag_type + index_from_count(get_government_type_count()), identifier, std::move(ideologies), elections, appoint_ruling_party, term_duration, flag_type ); /* flag_type can be empty here for default/non-ideological flag */ diff --git a/src/openvic-simulation/politics/Ideology.cpp b/src/openvic-simulation/politics/Ideology.cpp index 61067685e..23b4426b1 100644 --- a/src/openvic-simulation/politics/Ideology.cpp +++ b/src/openvic-simulation/politics/Ideology.cpp @@ -83,7 +83,7 @@ bool IdeologyManager::add_ideology( return false; } - const Ideology::index_t new_index = Ideology::index_t { ideologies.size() }; + const Ideology::index_t new_index = index_from_count(ideologies.size()); return ideologies.emplace_item( identifier, identifier, diff --git a/src/openvic-simulation/politics/IssueManager.cpp b/src/openvic-simulation/politics/IssueManager.cpp index 479c2b0ef..b219cdce8 100644 --- a/src/openvic-simulation/politics/IssueManager.cpp +++ b/src/openvic-simulation/politics/IssueManager.cpp @@ -14,7 +14,7 @@ bool IssueManager::add_party_policy_group(std::string_view identifier) { return party_policy_groups.emplace_item( identifier, - identifier, PartyPolicyGroup::index_t { get_party_policy_group_count() } + identifier, index_from_count(get_party_policy_group_count()) ); } @@ -29,7 +29,7 @@ bool IssueManager::add_party_policy( if (!party_policies.emplace_item( identifier, - PartyPolicy::index_t { get_party_policy_count() }, identifier, + index_from_count(get_party_policy_count()), identifier, new_colour, std::move(values), party_policy_group, std::move(rules), jingoism )) { return false; @@ -61,7 +61,7 @@ bool IssueManager::add_reform_group( if (!reform_groups.emplace_item( identifier, - identifier, ReformGroup::index_t { get_reform_group_count() }, reform_type, ordered, administrative + identifier, index_from_count(get_reform_group_count()), reform_type, ordered, administrative )) { return false; } @@ -112,7 +112,7 @@ bool IssueManager::add_reform( if (!reforms.emplace_item( identifier, - Reform::index_t { get_reform_count() }, identifier, + index_from_count(get_reform_count()), identifier, new_colour, std::move(values), reform_group, ordinal, administrative_multiplier, std::move(rules), technology_cost, std::move(allow), std::move(on_execute_trigger), std::move(on_execute_effect) )) { diff --git a/src/openvic-simulation/politics/Rebel.cpp b/src/openvic-simulation/politics/Rebel.cpp index df65fb2f2..a6a8a33fe 100644 --- a/src/openvic-simulation/politics/Rebel.cpp +++ b/src/openvic-simulation/politics/Rebel.cpp @@ -62,7 +62,7 @@ bool RebelManager::add_rebel_type( return rebel_types.emplace_item( new_identifier, - RebelType::index_t { get_rebel_type_count() }, new_identifier, + index_from_count(get_rebel_type_count()), new_identifier, icon, area, break_alliance_on_win, std::move(desired_governments), defection, independence, defect_delay, ideology, allow_all_cultures, allow_all_culture_groups, allow_all_religions, allow_all_ideologies, resilient, reinforcing, general, smart, unit_transfer, occupation_mult, std::move(will_rise), std::move(spawn_chance), diff --git a/src/openvic-simulation/population/Culture.cpp b/src/openvic-simulation/population/Culture.cpp index 3880ee4a0..979923955 100644 --- a/src/openvic-simulation/population/Culture.cpp +++ b/src/openvic-simulation/population/Culture.cpp @@ -11,7 +11,8 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -GraphicalCultureType::GraphicalCultureType(std::string_view new_identifier) : HasIdentifier { new_identifier } {} +GraphicalCultureType::GraphicalCultureType(std::string_view new_identifier, index_t new_index) + : HasIdentifier { new_identifier }, HasIndex { new_index } {} CultureGroup::CultureGroup( std::string_view new_identifier, std::string_view new_leader, GraphicalCultureType const& new_unit_graphical_culture_type, @@ -35,7 +36,7 @@ bool CultureManager::add_graphical_culture_type(std::string_view identifier) { } return graphical_culture_types.emplace_item( identifier, - identifier + identifier, index_from_count(get_graphical_culture_type_count()) ); } diff --git a/src/openvic-simulation/population/Culture.hpp b/src/openvic-simulation/population/Culture.hpp index 1b03ab969..28c55cfeb 100644 --- a/src/openvic-simulation/population/Culture.hpp +++ b/src/openvic-simulation/population/Culture.hpp @@ -3,6 +3,8 @@ #include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/UnitBranchType.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" +#include "openvic-simulation/types/HasIndex.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { struct CultureManager; @@ -10,11 +12,11 @@ namespace OpenVic { struct CountryDefinitionManager; class Dataloader; - struct GraphicalCultureType : HasIdentifier { + struct GraphicalCultureType : HasIdentifier, HasIndex { friend struct CultureManager; public: - GraphicalCultureType(std::string_view new_identifier); + GraphicalCultureType(std::string_view new_identifier, index_t new_index); GraphicalCultureType(GraphicalCultureType&&) = default; }; diff --git a/src/openvic-simulation/population/PopManager.cpp b/src/openvic-simulation/population/PopManager.cpp index 5210c84ef..0d1d86957 100644 --- a/src/openvic-simulation/population/PopManager.cpp +++ b/src/openvic-simulation/population/PopManager.cpp @@ -66,7 +66,7 @@ bool PopManager::add_strata(std::string_view identifier) { } return stratas.emplace_item( identifier, - identifier, Strata::index_t { get_strata_count() } + identifier, index_from_count(get_strata_count()) ); } @@ -157,7 +157,7 @@ bool PopManager::add_pop_type( identifier, identifier, colour, - PopType::index_t { get_pop_type_count() }, + index_from_count(get_pop_type_count()), *strata, sprite, std::move(life_needs), diff --git a/src/openvic-simulation/research/Invention.cpp b/src/openvic-simulation/research/Invention.cpp index 75a0b676a..4c0d9d769 100644 --- a/src/openvic-simulation/research/Invention.cpp +++ b/src/openvic-simulation/research/Invention.cpp @@ -55,7 +55,7 @@ bool InventionManager::add_invention( } return inventions.emplace_item( - identifier, Invention::index_t { get_invention_count() }, identifier, std::move(values), news, + identifier, index_from_count(get_invention_count()), identifier, std::move(values), news, std::move(activated_units), std::move(activated_buildings), std::move(enabled_crimes), unlock_gas_attack, unlock_gas_defence, std::move(limit), std::move(chance), std::move(raw_associated_tech_identifiers) diff --git a/src/openvic-simulation/research/Technology.cpp b/src/openvic-simulation/research/Technology.cpp index 12bc8fd91..2d341d9b2 100644 --- a/src/openvic-simulation/research/Technology.cpp +++ b/src/openvic-simulation/research/Technology.cpp @@ -55,7 +55,7 @@ bool TechnologyManager::add_technology_folder(std::string_view identifier) { return technology_folders.emplace_item( identifier, - identifier, TechnologyFolder::index_t { get_technology_folder_count() } + identifier, index_from_count(get_technology_folder_count()) ); } @@ -101,7 +101,7 @@ bool TechnologyManager::add_technology( if (!technologies.emplace_item( identifier, identifier, - Technology::index_t { get_technology_count() }, + index_from_count(get_technology_count()), *area, year, cost, diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp index 811fc6ecb..2464f242c 100644 --- a/src/openvic-simulation/types/IdentifierRegistry.hpp +++ b/src/openvic-simulation/types/IdentifierRegistry.hpp @@ -407,6 +407,26 @@ namespace OpenVic { ) CONST { \ return NodeTools::expect_identifier_or_string(expect_item_str(callback, allow_empty, warn), allow_empty); \ } \ + constexpr NodeTools::NodeCallback auto expect_item_index( \ + NodeTools::Callback auto callback, bool warn = false \ + ) CONST { \ + return expect_item_identifier( \ + [callback](external_value_type CONST& item) -> bool { \ + return callback(item.index); \ + }, \ + warn \ + ); \ + } \ + constexpr NodeTools::Callback auto expect_item_index_str( \ + NodeTools::Callback auto callback, bool allow_empty = false, bool warn = false \ + ) CONST { \ + return expect_item_str( \ + [callback](external_value_type CONST& item) -> bool { \ + return callback(item.index); \ + }, \ + allow_empty, warn \ + ); \ + } \ constexpr NodeTools::NodeCallback auto expect_item_assign_and_default( \ NodeTools::KeyValueCallback auto default_callback, \ NodeTools::Callback auto callback \ @@ -703,6 +723,17 @@ public: \ ) const_kw { \ return registry.expect_item_identifier(callback, warn); \ } \ + constexpr NodeTools::NodeCallback auto expect_##singular##_index( \ + NodeTools::Callback auto callback, bool warn = false \ + ) const_kw { \ + return registry.expect_item_index(callback, warn); \ + } \ + constexpr NodeTools::Callback auto expect_##singular##_index_str( \ + NodeTools::Callback auto callback, bool allow_empty = false, \ + bool warn = false \ + ) const_kw { \ + return registry.expect_item_index_str(callback, allow_empty, warn); \ + } \ constexpr NodeTools::NodeCallback auto expect_##singular##_string( \ NodeTools::Callback auto callback, bool allow_empty = false, \ bool warn = false \ diff --git a/src/openvic-simulation/types/IndexedFlatMap.hpp b/src/openvic-simulation/types/IndexedFlatMap.hpp index 09a3c89c9..90a0baef0 100644 --- a/src/openvic-simulation/types/IndexedFlatMap.hpp +++ b/src/openvic-simulation/types/IndexedFlatMap.hpp @@ -224,8 +224,8 @@ namespace OpenVic { keys_span_type new_keys, GeneratorTemplateType value_generator ) : keys(new_keys), - min_index { new_keys.front().index }, - max_index { new_keys.back().index }, + min_index { type_safe::get(new_keys.front().index) }, + max_index { type_safe::get(new_keys.back().index) }, values() { static_assert(has_index); if (!validate_new_keys(new_keys)) { @@ -264,8 +264,8 @@ namespace OpenVic { keys_span_type new_keys, GeneratorTemplateType value_generator ) : keys(new_keys), - min_index { new_keys.front().index }, - max_index { new_keys.back().index }, + min_index { type_safe::get(new_keys.front().index) }, + max_index { type_safe::get(new_keys.back().index) }, values { create_empty, new_keys.size() } { static_assert(has_index); if (!validate_new_keys(new_keys)) { @@ -310,8 +310,8 @@ namespace OpenVic { keys_span_type new_keys, GeneratorTemplateType&& value_generator ) : keys(new_keys), - min_index { new_keys.front().index }, - max_index { new_keys.back().index }, + min_index { type_safe::get(new_keys.front().index) }, + max_index { type_safe::get(new_keys.back().index) }, values() { static_assert(has_index); if (!validate_new_keys(new_keys)) { diff --git a/src/openvic-simulation/types/TypedIndices.hpp b/src/openvic-simulation/types/TypedIndices.hpp index a17451ed1..ab58df6d3 100644 --- a/src/openvic-simulation/types/TypedIndices.hpp +++ b/src/openvic-simulation/types/TypedIndices.hpp @@ -1,7 +1,9 @@ #pragma once +#include #include #include +#include #include #include @@ -27,15 +29,28 @@ return fmt::formatter::format(type_safe::get(value), ctx); \ } \ }; -#define TYPED_INDEX(name) TYPED_INDEX_CUSTOM(name, std::size_t) +#define TYPED_INDEX(name) TYPED_INDEX_CUSTOM(name, std::uint32_t) + +namespace OpenVic { + // Construct a typed index from a size_t count, asserts in debug + // builds that the count fits the index's underlying type + template + constexpr IndexT index_from_count(std::size_t count) { + using underlying_t = type_safe::underlying_type; + assert(count <= static_cast(std::numeric_limits::max())); + return IndexT { static_cast(count) }; + } +} TYPED_INDEX(bookmark_index_t) TYPED_INDEX(building_type_index_t) TYPED_INDEX(province_building_index_t) TYPED_INDEX(country_index_t) TYPED_INDEX(crime_index_t) +TYPED_INDEX(good_category_index_t) TYPED_INDEX(good_index_t) TYPED_INDEX(government_type_index_t) +TYPED_INDEX(graphical_culture_index_t) TYPED_INDEX(ideology_index_t) TYPED_INDEX(invention_index_t) TYPED_INDEX(map_mode_index_t) @@ -51,6 +66,7 @@ TYPED_INDEX(strata_index_t) TYPED_INDEX(technology_index_t) TYPED_INDEX(technology_folder_index_t) TYPED_INDEX(terrain_type_index_t) +TYPED_INDEX(unit_type_index_t) namespace OpenVic { struct province_index_t : type_safe::strong_typedef, diff --git a/tests/src/economy/trading/GoodMarket.cpp b/tests/src/economy/trading/GoodMarket.cpp index 169187553..0261db216 100644 --- a/tests/src/economy/trading/GoodMarket.cpp +++ b/tests/src/economy/trading/GoodMarket.cpp @@ -14,7 +14,7 @@ using namespace OpenVic; -GoodCategory good_category {"test_good_category"}; +GoodCategory good_category {"test_good_category", good_category_index_t{0}}; const fixed_point_t base_price = 16; constexpr bool is_not_available_from_start = false; diff --git a/tests/src/types/IdentifierRegistry.cpp b/tests/src/types/IdentifierRegistry.cpp new file mode 100644 index 000000000..f9e3f8770 --- /dev/null +++ b/tests/src/types/IdentifierRegistry.cpp @@ -0,0 +1,190 @@ +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +#include + +#include +#include + +#include +#include +#include + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/types/HasIdentifier.hpp" +#include "openvic-simulation/types/HasIndex.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" + +#include "Helper.hpp" +#include +#include + +using namespace OpenVic; +using namespace OpenVic::ast; +using namespace std::string_view_literals; + +namespace { + /* Minimal AST builder, mirroring tests/src/dataloader/NodeTools.cpp, for feeding + * identifier/string nodes into NodeTools callbacks without parsing files. */ + struct Ast : ovdl::SymbolIntern { + dryad::tree tree; + symbol_interner_type symbol_interner; + + template + T* create_with_intern(std::string_view view, Args&&... args) { + auto intern = symbol_interner.intern(view.data(), view.size()); + return tree.template create(intern, DRYAD_FWD(args)...); + } + }; + + /* Lightweight registry item carrying both a string identifier and a typed index. */ + struct TestItem : HasIdentifier, HasIndex { + TestItem(index_t new_index, std::string_view new_identifier) + : HasIdentifier { new_identifier }, HasIndex { new_index } {} + TestItem(TestItem&&) = default; + }; +} + +TEST_CASE("IdentifierRegistry add and retrieve", "[IdentifierRegistry]") { + IdentifierRegistry registry { "test" }; + + CHECK(registry.emplace_item("alpha", good_index_t { 0 }, "alpha")); + CHECK(registry.emplace_item("beta", good_index_t { 1 }, "beta")); + CHECK(registry.size() == 2); + + TestItem const* alpha = registry.get_item_by_identifier("alpha"); + REQUIRE(alpha != nullptr); + CHECK(alpha->get_identifier() == "alpha"sv); + CHECK(alpha->index == good_index_t { 0 }); + + TestItem const* beta = registry.get_item_by_identifier("beta"); + REQUIRE(beta != nullptr); + CHECK(beta->index == good_index_t { 1 }); + + CHECK(registry.get_item_by_identifier("gamma") == nullptr); + CHECK(registry.has_identifier("alpha")); + CHECK_FALSE(registry.has_identifier("gamma")); +} + +TEST_CASE("IdentifierRegistry rejects duplicate identifier", "[IdentifierRegistry]") { + IdentifierRegistry registry { "test" }; + + CHECK(registry.emplace_item("alpha", good_index_t { 0 }, "alpha")); + /* Duplicate uses the default duplicate_fail_callback, which returns false and logs an error. */ + CHECK_FALSE(registry.emplace_item("alpha", good_index_t { 1 }, "alpha")); + CHECK(registry.size() == 1); +} + +TEST_CASE("IdentifierRegistry lock blocks further adds", "[IdentifierRegistry]") { + IdentifierRegistry registry { "test", false }; + + CHECK(registry.emplace_item("alpha", good_index_t { 0 }, "alpha")); + CHECK_FALSE(registry.is_locked()); + + registry.lock(); + CHECK(registry.is_locked()); + + /* Adding after lock fails. */ + CHECK_FALSE(registry.emplace_item("beta", good_index_t { 1 }, "beta")); + CHECK(registry.size() == 1); +} + +TEST_CASE("IdentifierRegistry expect_item_identifier", "[IdentifierRegistry]") { + IdentifierRegistry registry { "test" }; + REQUIRE(registry.emplace_item("alpha", good_index_t { 0 }, "alpha")); + + Ast ast; + auto* alpha_id = ast.create_with_intern("alpha"sv); + + bool invoked = false; + CHECK(registry.expect_item_identifier( + [&invoked](TestItem const& item) -> bool { + invoked = true; + CHECK_RETURN_BOOL(item.get_identifier() == "alpha"sv); + } + )(alpha_id)); + CHECK(invoked); +} + +TEST_CASE("IdentifierRegistry expect_item_index", "[IdentifierRegistry]") { + IdentifierRegistry registry { "test" }; + REQUIRE(registry.emplace_item("alpha", good_index_t { 0 }, "alpha")); + REQUIRE(registry.emplace_item("beta", good_index_t { 1 }, "beta")); + + Ast ast; + auto* beta_id = ast.create_with_intern("beta"sv); + auto* unknown_id = ast.create_with_intern("gamma"sv); + + /* Known identifier delivers the item's typed index. */ + good_index_t result { 0 }; + bool invoked = false; + CHECK(registry.expect_item_index( + [&result, &invoked](good_index_t index) -> bool { + invoked = true; + result = index; + return true; + } + )(beta_id)); + CHECK(invoked); + CHECK(result == good_index_t { 1 }); + + /* Unknown identifier: callback not invoked, returns false without warn. */ + invoked = false; + CHECK_FALSE(registry.expect_item_index( + [&invoked](good_index_t) -> bool { + invoked = true; + return true; + } + )(unknown_id)); + CHECK_FALSE(invoked); + + /* Unknown identifier with warn = true returns true, callback still not invoked. */ + invoked = false; + CHECK(registry.expect_item_index( + [&invoked](good_index_t) -> bool { + invoked = true; + return true; + }, + true + )(unknown_id)); + CHECK_FALSE(invoked); +} + +TEST_CASE("IdentifierRegistry expect_item_index_str", "[IdentifierRegistry]") { + IdentifierRegistry registry { "test" }; + REQUIRE(registry.emplace_item("alpha", good_index_t { 0 }, "alpha")); + REQUIRE(registry.emplace_item("beta", good_index_t { 1 }, "beta")); + + /* Known key resolves to its typed index via the string path. */ + good_index_t result { 0 }; + bool invoked = false; + CHECK(registry.expect_item_index_str( + [&result, &invoked](good_index_t index) -> bool { + invoked = true; + result = index; + return true; + } + )("beta"sv)); + CHECK(invoked); + CHECK(result == good_index_t { 1 }); + + /* Unknown key: callback not invoked, returns false without warn. */ + invoked = false; + CHECK_FALSE(registry.expect_item_index_str( + [&invoked](good_index_t) -> bool { + invoked = true; + return true; + } + )("gamma"sv)); + CHECK_FALSE(invoked); + + /* Unknown key with warn = true returns true, callback still not invoked. */ + invoked = false; + CHECK(registry.expect_item_index_str( + [&invoked](good_index_t) -> bool { + invoked = true; + return true; + }, + false, true + )("gamma"sv)); + CHECK_FALSE(invoked); +} diff --git a/tests/src/types/TypedIndices.cpp b/tests/src/types/TypedIndices.cpp new file mode 100644 index 000000000..bbbf2c2db --- /dev/null +++ b/tests/src/types/TypedIndices.cpp @@ -0,0 +1,45 @@ +#include "openvic-simulation/types/TypedIndices.hpp" + +#include + +#include + +#include +#include + +using namespace OpenVic; + +static_assert(sizeof(bookmark_index_t) == 4); +static_assert(sizeof(building_type_index_t) == 4); +static_assert(sizeof(province_building_index_t) == 4); +static_assert(sizeof(country_index_t) == 4); +static_assert(sizeof(crime_index_t) == 4); +static_assert(sizeof(good_index_t) == 4); +static_assert(sizeof(good_category_index_t) == 4); +static_assert(sizeof(government_type_index_t) == 4); +static_assert(sizeof(graphical_culture_index_t) == 4); +static_assert(sizeof(ideology_index_t) == 4); +static_assert(sizeof(invention_index_t) == 4); +static_assert(sizeof(map_mode_index_t) == 4); +static_assert(sizeof(party_policy_index_t) == 4); +static_assert(sizeof(party_policy_group_index_t) == 4); +static_assert(sizeof(pop_type_index_t) == 4); +static_assert(sizeof(rebel_type_index_t) == 4); +static_assert(sizeof(reform_index_t) == 4); +static_assert(sizeof(reform_group_index_t) == 4); +static_assert(sizeof(regiment_type_index_t) == 4); +static_assert(sizeof(ship_type_index_t) == 4); +static_assert(sizeof(strata_index_t) == 4); +static_assert(sizeof(technology_index_t) == 4); +static_assert(sizeof(technology_folder_index_t) == 4); +static_assert(sizeof(terrain_type_index_t) == 4); +static_assert(sizeof(unit_type_index_t) == 4); +static_assert(sizeof(province_index_t) == 2); + +TEST_CASE("TypedIndices index_from_count round trip", "[TypedIndices]") { + CHECK(type_safe::get(index_from_count(0)) == 0); + CHECK(type_safe::get(index_from_count(42)) == 42); + CHECK(type_safe::get(index_from_count(65535)) == 65535); + CHECK(type_safe::get(index_from_count(100000)) == 100000); + CHECK(index_from_count(7) == ideology_index_t { static_cast(7) }); +}