diff options
Diffstat (limited to 'src')
24 files changed, 428 insertions, 318 deletions
diff --git a/src/openvic-simulation/country/Country.cpp b/src/openvic-simulation/country/Country.cpp index 299407c..463d1bf 100644 --- a/src/openvic-simulation/country/Country.cpp +++ b/src/openvic-simulation/country/Country.cpp @@ -1,10 +1,6 @@ #include "Country.hpp" -#include <filesystem> #include <string_view> -#include <system_error> -#include <unordered_map> -#include <vector> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> @@ -125,8 +121,7 @@ node_callback_t CountryManager::load_country_party( return politics_manager.get_issue_manager().expect_issue_identifier( [&policies, &group](Issue const& issue) -> bool { if (&issue.get_group() == &group) { - policies.emplace(&group, &issue); - return true; + return map_callback(policies, &group)(&issue); } // TODO - change this back to error/false once TGC no longer has this issue Logger::warning("Invalid policy ", issue.get_identifier(), ", group is ", @@ -161,19 +156,10 @@ bool CountryManager::load_country_data_file( Country::unit_names_map_t unit_names; Country::government_colour_map_t alternative_colours; bool ret = expect_dictionary_keys_and_default( - [&game_manager, &alternative_colours, &name](std::string_view key, ast::NodeCPtr value) -> bool { + [&game_manager, &alternative_colours](std::string_view key, ast::NodeCPtr value) -> bool { return game_manager.get_politics_manager().get_government_type_manager().expect_government_type_str( - [&alternative_colours, &name, &value](GovernmentType const& government_type) -> bool { - if (alternative_colours.contains(&government_type)) { - Logger::error( - "Country ", name, " has duplicate entry for ", government_type.get_identifier(), - " alternative colour" - ); - return false; - } - return expect_colour([&alternative_colours, &government_type](colour_t colour) -> bool { - return alternative_colours.emplace(&government_type, std::move(colour)).second; - })(value); + [&alternative_colours, value](GovernmentType const& government_type) -> bool { + return expect_colour(map_callback(alternative_colours, &government_type))(value); } )(key); }, @@ -184,15 +170,10 @@ bool CountryManager::load_country_data_file( ), "party", ZERO_OR_MORE, load_country_party(game_manager.get_politics_manager(), parties), "unit_names", ZERO_OR_ONE, - game_manager.get_military_manager().get_unit_manager().expect_unit_dictionary( - [&unit_names, &name](Unit const& unit, ast::NodeCPtr value) -> bool { - if (unit_names.contains(&unit)) { - Logger::error("Country ", name, " has duplicate entry for ", unit.get_identifier(), " name list"); - return false; - } - return name_list_callback([&unit_names, &unit](std::vector<std::string>&& list) -> bool { - return unit_names.emplace(&unit, std::move(list)).second; - })(value); + game_manager.get_military_manager().get_unit_manager().expect_unit_dictionary_reserve_length( + unit_names, + [&unit_names](Unit const& unit, ast::NodeCPtr value) -> bool { + return name_list_callback(map_callback(unit_names, &unit))(value); } ) )(root); diff --git a/src/openvic-simulation/country/Country.hpp b/src/openvic-simulation/country/Country.hpp index 76254a2..5f60534 100644 --- a/src/openvic-simulation/country/Country.hpp +++ b/src/openvic-simulation/country/Country.hpp @@ -51,7 +51,7 @@ namespace OpenVic { struct Country : HasIdentifierAndColour { friend struct CountryManager; - using unit_names_map_t = ordered_map<Unit const*, std::vector<std::string>>; + using unit_names_map_t = ordered_map<Unit const*, name_list_t>; using government_colour_map_t = ordered_map<GovernmentType const*, colour_t>; private: diff --git a/src/openvic-simulation/dataloader/Dataloader.hpp b/src/openvic-simulation/dataloader/Dataloader.hpp index 069ccec..de72fcd 100644 --- a/src/openvic-simulation/dataloader/Dataloader.hpp +++ b/src/openvic-simulation/dataloader/Dataloader.hpp @@ -2,7 +2,6 @@ #include <openvic-dataloader/csv/Parser.hpp> #include <openvic-dataloader/v2script/Parser.hpp> -#include <unordered_map> //keep this here or mac builds will fail #include "openvic-simulation/dataloader/NodeTools.hpp" diff --git a/src/openvic-simulation/dataloader/NodeTools.cpp b/src/openvic-simulation/dataloader/NodeTools.cpp index 4612f77..957aa01 100644 --- a/src/openvic-simulation/dataloader/NodeTools.cpp +++ b/src/openvic-simulation/dataloader/NodeTools.cpp @@ -400,9 +400,9 @@ node_callback_t NodeTools::expect_dictionary_key_map(key_map_t key_map) { ); } -node_callback_t NodeTools::name_list_callback(callback_t<std::vector<std::string>&&> callback) { +node_callback_t NodeTools::name_list_callback(callback_t<name_list_t&&> callback) { return [callback](ast::NodeCPtr node) -> bool { - std::vector<std::string> list; + name_list_t list; bool ret = expect_list_reserve_length( list, expect_identifier_or_string(vector_callback<std::string_view>(list)) )(node); @@ -411,6 +411,17 @@ node_callback_t NodeTools::name_list_callback(callback_t<std::vector<std::string }; } +std::ostream& OpenVic::operator<<(std::ostream& stream, name_list_t const& name_list) { + stream << '['; + if (!name_list.empty()) { + stream << name_list.front(); + std::for_each(name_list.begin() + 1, name_list.end(), [&stream](std::string const& name) -> void { + stream << ", " << name; + }); + } + return stream << ']'; +} + callback_t<std::string_view> NodeTools::assign_variable_callback_string(std::string& var) { return assign_variable_callback_cast<std::string_view>(var); } diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp index 00ba02d..b3fce16 100644 --- a/src/openvic-simulation/dataloader/NodeTools.hpp +++ b/src/openvic-simulation/dataloader/NodeTools.hpp @@ -11,6 +11,7 @@ #include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/types/Date.hpp" +#include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" #include "openvic-simulation/types/Vector.hpp" @@ -28,6 +29,9 @@ namespace OpenVic { using string_set_t = ordered_set<std::string>; using case_insensitive_string_set_t = case_insensitive_ordered_set<std::string>; + using name_list_t = std::vector<std::string>; + std::ostream& operator<<(std::ostream& stream, name_list_t const& name_list); + namespace NodeTools { template<typename Fn, typename Return = void, typename... Args> @@ -234,21 +238,22 @@ namespace OpenVic { t.reserve(size_t {}); }; template<Reservable T> + LengthCallback auto reserve_length_callback(T& t) { + return [&t](size_t size) -> size_t { + t.reserve(size); + return size; + }; + } + template<Reservable T> NodeCallback auto expect_list_reserve_length(T& t, NodeCallback auto callback) { - return expect_list_and_length( - [&t](size_t size) -> size_t { - t.reserve(t.size() + size); - return size; - }, - callback - ); + return expect_list_and_length(reserve_length_callback(t), callback); } template<Reservable T> NodeCallback auto expect_dictionary_reserve_length(T& t, KeyValueCallback auto callback) { return expect_list_reserve_length(t, expect_assign(callback)); } - node_callback_t name_list_callback(callback_t<std::vector<std::string>&&> callback); + node_callback_t name_list_callback(callback_t<name_list_t&&> callback); template<typename T, class Hash, class KeyEqual> Callback<std::string_view> auto expect_mapped_string( @@ -351,10 +356,32 @@ namespace OpenVic { }; } - template<typename T, typename...SetArgs> + template<typename T, typename U, typename...SetArgs> + Callback<T> auto set_callback(tsl::ordered_set<U, SetArgs...>& set) { + return [&set](T val) -> bool { + if (!set.emplace(std::move(val)).second) { + Logger::warning("Duplicate set entry: \"", val, "\""); + } + return true; + }; + } + + template<std::derived_from<HasIdentifier> T, typename...SetArgs> Callback<T const&> auto set_callback_pointer(tsl::ordered_set<T const*, SetArgs...>& set) { return [&set](T const& val) -> bool { - set.insert(&val); + if (!set.emplace(&val).second) { + Logger::warning("Duplicate set entry: \"", &val, "\""); + } + return true; + }; + } + + template<std::derived_from<HasIdentifier> Key, typename Value, typename... MapArgs> + Callback<Value> auto map_callback(tsl::ordered_map<Key const*, Value, MapArgs...>& map, Key const* key) { + return [&map, key](Value value) -> bool { + if (!map.emplace(key, std::move(value)).second) { + Logger::warning("Duplicate map entry with key: \"", key, "\""); + } return true; }; } diff --git a/src/openvic-simulation/diplomacy/DiplomaticAction.cpp b/src/openvic-simulation/diplomacy/DiplomaticAction.cpp index c1aa3df..d10eda3 100644 --- a/src/openvic-simulation/diplomacy/DiplomaticAction.cpp +++ b/src/openvic-simulation/diplomacy/DiplomaticAction.cpp @@ -9,13 +9,12 @@ using namespace OpenVic; DiplomaticActionType::DiplomaticActionType(DiplomaticActionType::Initializer&& initializer) - : commit_action_caller { std::move(initializer.commit) }, - allowed_to_commit { std::move(initializer.allowed) }, get_acceptance { std::move(initializer.get_acceptance) } {} + : commit_action_caller { std::move(initializer.commit) }, + allowed_to_commit { std::move(initializer.allowed) }, get_acceptance { std::move(initializer.get_acceptance) } {} CancelableDiplomaticActionType::CancelableDiplomaticActionType(CancelableDiplomaticActionType::Initializer&& initializer) : allowed_to_cancel{std::move(initializer.allowed_cancel)}, DiplomaticActionType(std::move(initializer)) {} - DiplomaticActionManager::DiplomaticActionManager() {} bool DiplomaticActionManager::add_diplomatic_action( @@ -49,7 +48,7 @@ DiplomaticActionTickCache DiplomaticActionManager::create_diplomatic_action_tick result.acceptance = type.get_acceptance(result.argument); } }); - + return result; } @@ -78,38 +77,38 @@ bool DiplomaticActionManager::setup_diplomatic_actions() { } ); result &= add_diplomatic_action( - "give_military_access", + "give_military_access", { [](Argument& arg) {} } ); result &= add_diplomatic_action( - "increase_relations", + "increase_relations", { .commit = [](Argument& arg) {}, .allowed = [](const Argument& arg) { return false; }, } ); result &= add_diplomatic_action( - "decrease_relations", + "decrease_relations", { [](Argument& arg) {} } ); result &= add_diplomatic_action( - "war_subsidies", + "war_subsidies", { [](Argument& arg) {} } ); result &= add_diplomatic_action( - "declare_war", + "declare_war", { [](Argument& arg) {} } ); result &= add_diplomatic_action( - "offer_peace", + "offer_peace", { [](Argument& arg) {} } ); result &= add_diplomatic_action( - "command_units", + "command_units", { [](Argument& arg) {} } ); result &= add_diplomatic_action( - "discredit", + "discredit", { [](Argument& arg) {} } ); result &= add_diplomatic_action( diff --git a/src/openvic-simulation/history/CountryHistory.cpp b/src/openvic-simulation/history/CountryHistory.cpp index 96a0b7c..3b4dfd8 100644 --- a/src/openvic-simulation/history/CountryHistory.cpp +++ b/src/openvic-simulation/history/CountryHistory.cpp @@ -1,7 +1,6 @@ #include "CountryHistory.hpp" #include "openvic-simulation/GameManager.hpp" -#include <string_view> using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -28,22 +27,20 @@ bool CountryHistoryMap::_load_history_entry( DecisionManager const& decision_manager = game_manager.get_decision_manager(); return expect_dictionary_keys_and_default( - [this, &game_manager, &dataloader, &deployment_manager, &issue_manager, - &technology_manager, &invention_manager, &country_manager, &entry](std::string_view key, ast::NodeCPtr value) -> bool { + [this, &game_manager, &dataloader, &deployment_manager, &issue_manager, &technology_manager, &invention_manager, + &country_manager, &entry](std::string_view key, ast::NodeCPtr value) -> bool { ReformGroup const* reform_group = issue_manager.get_reform_group_by_identifier(key); if (reform_group != nullptr) { return issue_manager.expect_reform_identifier([&entry, reform_group](Reform const& reform) -> bool { if (&reform.get_reform_group() != reform_group) { Logger::warning( - "Listing ", reform.get_identifier(), " as belonging to the reform group ", - reform_group->get_identifier(), " when it actually belongs to ", - reform.get_reform_group().get_identifier(), " in history of ", entry.get_country().get_identifier() + "Listing ", reform.get_identifier(), " as belonging to the reform group ", reform_group, + " when it actually belongs to ", reform.get_reform_group(), " in history of ", entry.get_country() ); } if (std::find(entry.reforms.begin(), entry.reforms.end(), &reform) != entry.reforms.end()) { Logger::error( - "Redefinition of reform ", reform.get_identifier(), " in history of ", - entry.get_country().get_identifier() + "Redefinition of reform ", reform.get_identifier(), " in history of ", entry.get_country() ); return false; } @@ -55,36 +52,14 @@ bool CountryHistoryMap::_load_history_entry( { Technology const* technology = technology_manager.get_technology_by_identifier(key); if (technology != nullptr) { - return expect_int_bool( - [&entry, technology](bool flag) -> bool { - if (!entry.technologies.emplace(technology, flag).second) { - Logger::error( - "Duplicate entry for technology ", technology->get_identifier(), " in history of ", - entry.get_country().get_identifier(), " at date ", entry.get_date() - ); - return false; - } - return true; - } - )(value); + return expect_int_bool(map_callback(entry.technologies, technology))(value); } } { Invention const* invention = invention_manager.get_invention_by_identifier(key); if (invention != nullptr) { - return expect_bool( - [&entry, invention](bool flag) -> bool { - if (!entry.inventions.emplace(invention, flag).second) { - Logger::error( - "Duplicate entry for invention ", invention->get_identifier(), " in history of ", - entry.get_country().get_identifier(), " at date ", entry.get_date() - ); - return false; - } - return true; - } - )(value); + return expect_bool(map_callback(entry.inventions, invention))(value); } } @@ -113,12 +88,10 @@ bool CountryHistoryMap::_load_history_entry( "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.prestige)), "ruling_party", ZERO_OR_ONE, country.expect_party_identifier(assign_variable_callback_pointer(entry.ruling_party)), "last_election", ZERO_OR_ONE, expect_date(assign_variable_callback(entry.last_election)), - "upper_house", ZERO_OR_ONE, politics_manager.get_ideology_manager().expect_ideology_dictionary( + "upper_house", ZERO_OR_ONE, politics_manager.get_ideology_manager().expect_ideology_dictionary_reserve_length( + entry.upper_house, [&entry](Ideology const& ideology, ast::NodeCPtr value) -> bool { - return expect_fixed_point([&entry, &ideology](fixed_point_t val) -> bool { - entry.upper_house[&ideology] = val; - return true; - })(value); + return expect_fixed_point(map_callback(entry.upper_house, &ideology))(value); } ), "oob", ZERO_OR_ONE, expect_identifier_or_string( @@ -134,9 +107,11 @@ bool CountryHistoryMap::_load_history_entry( "schools", ZERO_OR_ONE, technology_manager.expect_technology_school_identifier( assign_variable_callback_pointer(entry.tech_school) ), - "foreign_investment", ZERO_OR_ONE, country_manager.expect_country_decimal_map(move_variable_callback(entry.foreign_investment)), + "foreign_investment", ZERO_OR_ONE, + country_manager.expect_country_decimal_map(move_variable_callback(entry.foreign_investment)), "literacy", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.literacy)), - "non_state_culture_literacy", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.nonstate_culture_literacy)), + "non_state_culture_literacy", ZERO_OR_ONE, + expect_fixed_point(assign_variable_callback(entry.nonstate_culture_literacy)), "consciousness", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.consciousness)), "nonstate_consciousness", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.nonstate_consciousness)), "is_releasable_vassal", ZERO_OR_ONE, expect_bool(assign_variable_callback(entry.releasable_vassal)), @@ -153,7 +128,7 @@ bool CountryHistoryMap::_load_history_entry( if (flag_expected) { Logger::error( "Government key found when expect flag type override for ", government_type, - " in history of ", entry.get_country().get_identifier() + " in history of ", entry.get_country() ); ret = false; } @@ -173,22 +148,19 @@ bool CountryHistoryMap::_load_history_entry( /* If the first government type is null, the "government" section will have already output * an error, so no need to output another one here. */ if (government_type != nullptr && flag_override_government_type != nullptr) { - ret &= entry.government_flag_overrides.emplace( - government_type, flag_override_government_type - ).second; + ret &= map_callback(entry.government_flag_overrides, government_type)(flag_override_government_type); } return ret; } else { Logger::error( "Flag key found when expecting government type for flag type override in history of ", - entry.get_country().get_identifier() + entry.get_country() ); return false; } } else { Logger::error( - "Invalid key ", id, " in government flag overrides in history of ", - entry.get_country().get_identifier() + "Invalid key ", id, " in government flag overrides in history of ", entry.get_country() ); return false; } @@ -196,20 +168,15 @@ bool CountryHistoryMap::_load_history_entry( )(value); if (flag_expected) { Logger::error( - "Missing flag type override for government type ", government_type, " in history of ", - entry.get_country().get_identifier() + "Missing flag type override for government type ", government_type, " in history of ", entry.get_country() ); ret = false; } return ret; }, "colonial_points", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.colonial_points)), - "set_country_flag", ZERO_OR_MORE, expect_identifier_or_string([&entry](std::string_view flag) -> bool { - return entry.country_flags.emplace(flag).second; - }), - "set_global_flag", ZERO_OR_MORE, expect_identifier_or_string([&entry](std::string_view flag) -> bool { - return entry.global_flags.emplace(flag).second; - }) + "set_country_flag", ZERO_OR_MORE, expect_identifier_or_string(set_callback<std::string_view>(entry.country_flags)), + "set_global_flag", ZERO_OR_MORE, expect_identifier_or_string(set_callback<std::string_view>(entry.global_flags)) )(root); } @@ -231,7 +198,7 @@ CountryHistoryMap const* CountryHistoryManager::get_country_history(Country cons if (country_registry != country_histories.end()) { return &country_registry->second; } else { - Logger::error("Attempted to access history of country ", country->get_identifier(), " but none has been defined!"); + Logger::error("Attempted to access history of country ", country, " but none has been defined!"); return nullptr; } } @@ -240,10 +207,7 @@ bool CountryHistoryManager::load_country_history_file( GameManager& game_manager, Dataloader const& dataloader, Country const& country, ast::NodeCPtr root ) { if (locked) { - Logger::error( - "Attempted to load country history file for ", country.get_identifier(), - " after country history registry was locked!" - ); + Logger::error("Attempted to load country history file for ", country, " after country history registry was locked!"); return false; } @@ -258,7 +222,7 @@ bool CountryHistoryManager::load_country_history_file( if (result.second) { it = result.first; } else { - Logger::error("Failed to create country history map for country ", country.get_identifier()); + Logger::error("Failed to create country history map for country ", country); return false; } } diff --git a/src/openvic-simulation/history/ProvinceHistory.cpp b/src/openvic-simulation/history/ProvinceHistory.cpp index f5e5187..4117450 100644 --- a/src/openvic-simulation/history/ProvinceHistory.cpp +++ b/src/openvic-simulation/history/ProvinceHistory.cpp @@ -35,10 +35,15 @@ bool ProvinceHistoryMap::_load_history_entry( // used for province buildings like forts or railroads BuildingType const* building_type = building_type_manager.get_building_type_by_identifier(key); if (building_type != nullptr) { - return expect_uint<BuildingType::level_t>([&entry, building_type](BuildingType::level_t level) -> bool { - entry.province_buildings[building_type] = level; - return true; - })(value); + if (building_type->is_in_province()) { + return expect_uint<BuildingType::level_t>(map_callback(entry.province_buildings, building_type))(value); + } else { + Logger::error( + "Attempted to add state building \"", building_type, "\" at top scope of province history for ", + entry.get_province() + ); + return false; + } } return _load_history_sub_entry_callback(game_manager, entry.get_date(), value, key, value); @@ -65,27 +70,39 @@ bool ProvinceHistoryMap::_load_history_entry( Ideology const* ideology = nullptr; fixed_point_t amount = 0; // percent I do believe - const bool ret = expect_dictionary_keys( + bool ret = expect_dictionary_keys( "ideology", ONE_EXACTLY, ideology_manager.expect_ideology_identifier( assign_variable_callback_pointer(ideology) ), "loyalty_value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(amount)) )(node); - entry.party_loyalties[ideology] = amount; + if (ideology != nullptr) { + ret &= map_callback(entry.party_loyalties, ideology)(amount); + } return ret; }, "state_building", ZERO_OR_MORE, [&building_type_manager, &entry](ast::NodeCPtr node) -> bool { BuildingType const* building_type = nullptr; uint8_t level = 0; - const bool ret = expect_dictionary_keys( + bool ret = expect_dictionary_keys( "level", ONE_EXACTLY, expect_uint(assign_variable_callback(level)), "building", ONE_EXACTLY, building_type_manager.expect_building_type_identifier( assign_variable_callback_pointer(building_type) ), "upgrade", ZERO_OR_ONE, success_callback // doesn't appear to have an effect )(node); - entry.state_buildings[building_type] = level; + if (building_type != nullptr) { + if (!building_type->is_in_province()) { + ret &= map_callback(entry.state_buildings, building_type)(level); + } else { + Logger::error( + "Attempted to add province building \"", building_type, "\" to state building list of province history for ", + entry.get_province() + ); + ret = false; + } + } return ret; } )(root); @@ -174,7 +191,8 @@ bool ProvinceHistoryEntry::_load_province_pop_history( ) { PopManager const& pop_manager = game_manager.get_pop_manager(); RebelManager const& rebel_manager = game_manager.get_politics_manager().get_rebel_manager(); - return pop_manager.expect_pop_type_dictionary( + return pop_manager.expect_pop_type_dictionary_reserve_length( + pops, [this, &pop_manager, &rebel_manager, non_integer_size](PopType const& pop_type, ast::NodeCPtr pop_node) -> bool { return pop_manager.load_pop_into_vector(rebel_manager, pops, pop_type, pop_node, non_integer_size); } diff --git a/src/openvic-simulation/map/Map.cpp b/src/openvic-simulation/map/Map.cpp index 27079f4..1415e2c 100644 --- a/src/openvic-simulation/map/Map.cpp +++ b/src/openvic-simulation/map/Map.cpp @@ -893,6 +893,8 @@ bool Map::load_continent_file(ModifierManager const& modifier_manager, ast::Node [&prov_list](Province const& province) -> bool { if (province.continent == nullptr) { prov_list.emplace_back(&province); + } else { + Logger::warning("Province ", province, " found in multiple continents"); } return true; } diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp index 1c3c76f..acabbf8 100644 --- a/src/openvic-simulation/map/Province.cpp +++ b/src/openvic-simulation/map/Province.cpp @@ -36,12 +36,10 @@ bool Province::load_positions(BuildingTypeManager const& building_type_manager, "factory", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(positions.factory)), "building_construction", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(positions.building_construction)), "military_construction", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(positions.military_construction)), - "building_position", ZERO_OR_ONE, building_type_manager.expect_building_type_dictionary( + "building_position", ZERO_OR_ONE, building_type_manager.expect_building_type_dictionary_reserve_length( + positions.building_position, [this](BuildingType const& type, ast::NodeCPtr value) -> bool { - return expect_fvec2([this, &type](fvec2_t position) -> bool { - positions.building_position.emplace(&type, std::move(position)); - return true; - })(value); + return expect_fvec2(map_callback(positions.building_position, &type))(value); } ), "building_rotation", ZERO_OR_ONE, building_type_manager.expect_building_type_decimal_map( diff --git a/src/openvic-simulation/military/Wargoal.cpp b/src/openvic-simulation/military/Wargoal.cpp index 174cd09..6351ce3 100644 --- a/src/openvic-simulation/military/Wargoal.cpp +++ b/src/openvic-simulation/military/Wargoal.cpp @@ -122,10 +122,15 @@ bool WargoalTypeManager::load_wargoal_file(ast::NodeCPtr root) { }; const decltype(peace_modifier_map)::const_iterator it = peace_modifier_map.find(key); if (it != peace_modifier_map.end()) { - return expect_fixed_point([&modifiers, peace_modifier = it->second](fixed_point_t val) -> bool { - modifiers[peace_modifier] = val; - return true; - })(value); + return expect_fixed_point( + [&modifiers, &identifier, &key, peace_modifier = it->second](fixed_point_t val) -> bool { + if (modifiers.emplace(peace_modifier, val).second) { + return true; + } + Logger::error("Duplicate peace modifier ", key, " in wargoal ", identifier, "!"); + return false; + } + )(value); } Logger::error("Modifier ", key, " in wargoal ", identifier, " is invalid."); diff --git a/src/openvic-simulation/misc/Event.cpp b/src/openvic-simulation/misc/Event.cpp index eb5f7da..d6750c1 100644 --- a/src/openvic-simulation/misc/Event.cpp +++ b/src/openvic-simulation/misc/Event.cpp @@ -196,7 +196,9 @@ bool EventManager::load_event_file(IssueManager const& issue_manager, ast::NodeC bool EventManager::load_on_action_file(ast::NodeCPtr root) { bool ret = expect_dictionary([this](std::string_view identifier, ast::NodeCPtr node) -> bool { OnAction::weight_map_t weighted_events; - bool ret = expect_dictionary([this, &identifier, &weighted_events](std::string_view weight_str, ast::NodeCPtr event_node) -> bool { + bool ret = expect_dictionary_reserve_length( + weighted_events, + [this, &identifier, &weighted_events](std::string_view weight_str, ast::NodeCPtr event_node) -> bool { bool ret = false; uint64_t weight = StringUtils::string_to_uint64(weight_str, &ret); if (!ret) { @@ -208,7 +210,7 @@ bool EventManager::load_on_action_file(ast::NodeCPtr root) { ret &= expect_event_identifier(assign_variable_callback_pointer(event))(event_node); if (event != nullptr) { - ret &= weighted_events.emplace(event, weight).second; + ret &= map_callback(weighted_events, event)(weight); } else { Logger::warning( "Non-existing event ", event->get_identifier(), " loaded on action ", identifier, "with weight", diff --git a/src/openvic-simulation/misc/Modifier.cpp b/src/openvic-simulation/misc/Modifier.cpp index c83fcf0..2d95f0d 100644 --- a/src/openvic-simulation/misc/Modifier.cpp +++ b/src/openvic-simulation/misc/Modifier.cpp @@ -372,12 +372,7 @@ key_value_callback_t ModifierManager::_modifier_effect_callback( ) const { const auto add_modifier_cb = [this, &modifier, effect_validator](ModifierEffect const* effect, ast::NodeCPtr value) -> bool { if (effect_validator(*effect)) { - if (!modifier.values.contains(effect)) { - return expect_fixed_point(assign_variable_callback(modifier.values[effect]))(value); - } else { - Logger::error("Duplicate modifier effect: ", effect->get_identifier()); - return false; - } + return expect_fixed_point(map_callback(modifier.values, effect))(value); } else { Logger::error("Failed to validate modifier effect: ", effect->get_identifier()); return false; @@ -424,7 +419,10 @@ node_callback_t ModifierManager::expect_validated_modifier_value_and_default( ) const { return [this, modifier_callback, default_callback, effect_validator](ast::NodeCPtr root) -> bool { ModifierValue modifier; - bool ret = expect_dictionary(_modifier_effect_callback(modifier, default_callback, effect_validator))(root); + bool ret = expect_dictionary_reserve_length( + modifier.values, + _modifier_effect_callback(modifier, default_callback, effect_validator) + )(root); ret &= modifier_callback(std::move(modifier)); return ret; }; diff --git a/src/openvic-simulation/politics/NationalFocus.cpp b/src/openvic-simulation/politics/NationalFocus.cpp index e509039..c2084be 100644 --- a/src/openvic-simulation/politics/NationalFocus.cpp +++ b/src/openvic-simulation/politics/NationalFocus.cpp @@ -11,16 +11,18 @@ NationalFocus::NationalFocus( NationalFocusGroup const& new_group, ModifierValue&& new_modifiers, pop_promotion_map_t&& new_encouraged_promotion, - party_loyalty_map_t&& new_encouraged_loyalty, production_map_t&& new_encouraged_production, + Ideology const* new_loyalty_ideology, + fixed_point_t new_loyalty_value, ConditionScript&& new_limit ) : HasIdentifier { new_identifier }, icon { new_icon }, group { new_group }, modifiers { std::move(new_modifiers) }, encouraged_promotion { std::move(new_encouraged_promotion) }, - encouraged_loyalty { std::move(new_encouraged_loyalty) }, encouraged_production { std::move(new_encouraged_production) }, + loyalty_ideology { new_loyalty_ideology }, + loyalty_value { new_loyalty_value }, limit { std::move(new_limit) } {} bool NationalFocus::parse_scripts(GameManager const& game_manager) { @@ -41,8 +43,9 @@ inline bool NationalFocusManager::add_national_focus( NationalFocusGroup const& group, ModifierValue&& modifiers, NationalFocus::pop_promotion_map_t&& encouraged_promotion, - NationalFocus::party_loyalty_map_t&& encouraged_loyalty, NationalFocus::production_map_t&& encouraged_production, + Ideology const* loyalty_ideology, + fixed_point_t loyalty_value, ConditionScript&& limit ) { if (identifier.empty()) { @@ -53,74 +56,79 @@ inline bool NationalFocusManager::add_national_focus( Logger::error("Invalid icon ", icon, " for national focus ", identifier); return false; } + if ((loyalty_ideology == nullptr) != (loyalty_value == 0)) { + Logger::warning( + "Party loyalty incorrectly defined for national focus ", identifier, ": ideology = ", loyalty_ideology, + ", value = ", loyalty_value + ); + } return national_foci.add_item({ - identifier, icon, group, std::move(modifiers), std::move(encouraged_promotion), std::move(encouraged_loyalty), - std::move(encouraged_production), std::move(limit) + identifier, icon, group, std::move(modifiers), std::move(encouraged_promotion), std::move(encouraged_production), + loyalty_ideology, loyalty_value, std::move(limit) }); } -bool NationalFocusManager::load_national_foci_file(PopManager const& pop_manager, IdeologyManager const& ideology_manager, GoodManager const& good_manager, ModifierManager const& modifier_manager, ast::NodeCPtr root) { - bool ret = expect_dictionary_reserve_length(national_focus_groups, [this](std::string_view identifier, ast::NodeCPtr node) -> bool { - return add_national_focus_group(identifier); - })(root); +bool NationalFocusManager::load_national_foci_file( + PopManager const& pop_manager, IdeologyManager const& ideology_manager, GoodManager const& good_manager, + ModifierManager const& modifier_manager, ast::NodeCPtr root +) { + size_t expected_national_foci = 0; + bool ret = expect_dictionary_reserve_length( + national_focus_groups, + [this, &expected_national_foci](std::string_view identifier, ast::NodeCPtr node) -> bool { + return expect_length(add_variable_callback(expected_national_foci))(node) & add_national_focus_group(identifier); + } + )(root); lock_national_focus_groups(); - ret &= expect_national_focus_group_dictionary([this, &pop_manager, &ideology_manager, &good_manager, &modifier_manager](NationalFocusGroup const& group, ast::NodeCPtr node) -> bool { - bool ret = expect_dictionary([this, &group, &pop_manager, &ideology_manager, &good_manager, &modifier_manager](std::string_view identifier, ast::NodeCPtr node) -> bool { - uint8_t icon; + national_foci.reserve(expected_national_foci); + + ret &= expect_national_focus_group_dictionary([this, &pop_manager, &ideology_manager, &good_manager, &modifier_manager]( + NationalFocusGroup const& group, ast::NodeCPtr group_node + ) -> bool { + return expect_dictionary([this, &group, &pop_manager, &ideology_manager, &good_manager, &modifier_manager]( + std::string_view identifier, ast::NodeCPtr node + ) -> bool { + uint8_t icon = 0; ModifierValue modifiers; NationalFocus::pop_promotion_map_t promotions; - NationalFocus::party_loyalty_map_t loyalties; NationalFocus::production_map_t production; + Ideology const* loyalty_ideology = nullptr; + fixed_point_t loyalty_value = 0; ConditionScript limit { scope_t::PROVINCE | scope_t::COUNTRY, scope_t::PROVINCE | scope_t::COUNTRY, scope_t::NO_SCOPE }; - Ideology const* last_specified_ideology = nullptr; // weird, I know - bool ret = modifier_manager.expect_modifier_value_and_keys_and_default( move_variable_callback(modifiers), - [&promotions, &pop_manager, &production, &good_manager, &modifiers, &modifier_manager](std::string_view key, ast::NodeCPtr value) -> bool { + [&promotions, &pop_manager, &production, &good_manager, &modifiers, &modifier_manager]( + std::string_view key, ast::NodeCPtr value + ) -> bool { PopType const* pop_type = pop_manager.get_pop_type_by_identifier(key); if (pop_type != nullptr) { - fixed_point_t boost; - bool ret = expect_fixed_point(assign_variable_callback(boost))(value); - promotions[pop_type] = boost; - return ret; + return expect_fixed_point(map_callback(promotions, pop_type))(value); } Good const* good = good_manager.get_good_by_identifier(key); if (good != nullptr) { - fixed_point_t boost; - bool ret = expect_fixed_point(assign_variable_callback(boost))(value); - production[good] = boost; - return ret; + return expect_fixed_point(map_callback(production, good))(value); } return key_value_invalid_callback(key, value); }, "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon)), - "ideology", ZERO_OR_MORE, ideology_manager.expect_ideology_identifier(assign_variable_callback_pointer(last_specified_ideology)), - "loyalty_value", ZERO_OR_MORE, [&identifier, &last_specified_ideology, &loyalties](ast::NodeCPtr value) -> bool { - if (last_specified_ideology == nullptr) { - Logger::error("In national focus ", identifier, ": No ideology selected for loyalty_value!"); - return false; - } - fixed_point_t boost; - bool ret = expect_fixed_point(assign_variable_callback(boost))(value); - loyalties[last_specified_ideology] += boost; - return ret; - }, + "ideology", ZERO_OR_ONE, + ideology_manager.expect_ideology_identifier(assign_variable_callback_pointer(loyalty_ideology)), + "loyalty_value", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(loyalty_value)), "limit", ZERO_OR_ONE, limit.expect_script(), "has_flashpoint", ZERO_OR_ONE, success_callback, // special case, include in limit "own_provinces", ZERO_OR_ONE, success_callback, // special case, include in limit "outliner_show_as_percent", ZERO_OR_ONE, success_callback // special case )(node); - add_national_focus( - identifier, icon, group, std::move(modifiers), std::move(promotions), std::move(loyalties), - std::move(production), std::move(limit) + ret &= add_national_focus( + identifier, icon, group, std::move(modifiers), std::move(promotions), std::move(production), + loyalty_ideology, loyalty_value, std::move(limit) ); return ret; - })(node); - return ret; + })(group_node); })(root); lock_national_foci(); diff --git a/src/openvic-simulation/politics/NationalFocus.hpp b/src/openvic-simulation/politics/NationalFocus.hpp index 44e58e3..6e064f1 100644 --- a/src/openvic-simulation/politics/NationalFocus.hpp +++ b/src/openvic-simulation/politics/NationalFocus.hpp @@ -23,7 +23,6 @@ namespace OpenVic { public: using pop_promotion_map_t = fixed_point_map_t<PopType const*>; - using party_loyalty_map_t = fixed_point_map_t<Ideology const*>; using production_map_t = fixed_point_map_t<Good const*>; private: @@ -31,8 +30,9 @@ namespace OpenVic { NationalFocusGroup const& PROPERTY(group); ModifierValue PROPERTY(modifiers); pop_promotion_map_t PROPERTY(encouraged_promotion); - party_loyalty_map_t PROPERTY(encouraged_loyalty); production_map_t PROPERTY(encouraged_production); + Ideology const* PROPERTY(loyalty_ideology); + fixed_point_t PROPERTY(loyalty_value); ConditionScript PROPERTY(limit); NationalFocus( @@ -41,8 +41,9 @@ namespace OpenVic { NationalFocusGroup const& new_group, ModifierValue&& new_modifiers, pop_promotion_map_t&& new_encouraged_promotion, - party_loyalty_map_t&& new_encouraged_loyalty, production_map_t&& new_encouraged_production, + Ideology const* new_loyalty_ideology, + fixed_point_t new_loyalty_value, ConditionScript&& new_limit ); @@ -66,8 +67,9 @@ namespace OpenVic { NationalFocusGroup const& group, ModifierValue&& modifiers, NationalFocus::pop_promotion_map_t&& encouraged_promotion, - NationalFocus::party_loyalty_map_t&& encouraged_loyalty, NationalFocus::production_map_t&& encouraged_production, + Ideology const* loyalty_ideology, + fixed_point_t loyalty_value, ConditionScript&& limit ); diff --git a/src/openvic-simulation/politics/Rebel.cpp b/src/openvic-simulation/politics/Rebel.cpp index 6370786..f86bb51 100644 --- a/src/openvic-simulation/politics/Rebel.cpp +++ b/src/openvic-simulation/politics/Rebel.cpp @@ -115,17 +115,14 @@ bool RebelManager::load_rebels_file( "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon)), "area", ONE_EXACTLY, expect_identifier(expect_mapped_string(area_map, assign_variable_callback(area))), "break_alliance_on_win", ZERO_OR_ONE, expect_bool(assign_variable_callback(break_alliance_on_win)), - "government", ONE_EXACTLY, government_type_manager.expect_government_type_dictionary( - [this, &government_type_manager, &desired_governments](GovernmentType const& from, - ast::NodeCPtr value) -> bool { - if (desired_governments.contains(&from)) { - Logger::error("Duplicate \"from\" government type in rebel type: ", from.get_identifier()); - return false; - } + "government", ONE_EXACTLY, government_type_manager.expect_government_type_dictionary_reserve_length( + desired_governments, + [this, &government_type_manager, &desired_governments]( + GovernmentType const& from, ast::NodeCPtr value + ) -> bool { return government_type_manager.expect_government_type_identifier( [&desired_governments, &from](GovernmentType const& to) -> bool { - desired_governments.emplace(&from, &to); - return true; + return map_callback(desired_governments, &from)(&to); } )(value); } diff --git a/src/openvic-simulation/politics/Rule.cpp b/src/openvic-simulation/politics/Rule.cpp index 9ac992a..19e92af 100644 --- a/src/openvic-simulation/politics/Rule.cpp +++ b/src/openvic-simulation/politics/Rule.cpp @@ -109,8 +109,8 @@ bool RuleSet::set_rule(Rule const* rule, bool value) { Logger::error("Invalid rule - null!"); return false; } - rule_map_t& rule_map = rule_groups[rule->get_group()]; - return rule_groups[rule->get_group()].emplace(rule, value).second; + rule_groups[rule->get_group()][rule] = value; + return true; } RuleSet& RuleSet::operator|=(RuleSet const& right) { @@ -193,10 +193,9 @@ node_callback_t RuleManager::expect_rule_set(callback_t<RuleSet&&> ruleset_callb if (rule != nullptr) { return expect_bool( [&ruleset, rule](bool value) -> bool { - if (!ruleset.rule_groups[rule->get_group()].emplace(rule, value).second) { - Logger::warning("Duplicate rule entry: ", rule, " - ignoring!"); - } - return true; + /* Wrapped in a lambda function so that the rule group is only initialised + * if the value bool is successfully parsed. */ + return map_callback(ruleset.rule_groups[rule->get_group()], rule)(value); } )(rule_value); } else { diff --git a/src/openvic-simulation/pop/Culture.cpp b/src/openvic-simulation/pop/Culture.cpp index 9b9d6c2..2cd6005 100644 --- a/src/openvic-simulation/pop/Culture.cpp +++ b/src/openvic-simulation/pop/Culture.cpp @@ -16,7 +16,7 @@ CultureGroup::CultureGroup( Culture::Culture( std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, - std::vector<std::string>&& new_first_names, std::vector<std::string>&& new_last_names + name_list_t&& new_first_names, name_list_t&& new_last_names ) : HasIdentifierAndColour { new_identifier, new_colour, false }, group { new_group }, first_names { std::move(new_first_names) }, last_names { std::move(new_last_names) } {} @@ -51,8 +51,8 @@ bool CultureManager::add_culture_group( } bool CultureManager::add_culture( - std::string_view identifier, colour_t colour, CultureGroup const& group, std::vector<std::string>&& first_names, - std::vector<std::string>&& last_names + std::string_view identifier, colour_t colour, CultureGroup const& group, name_list_t&& first_names, + name_list_t&& last_names ) { if (!culture_groups.is_locked()) { Logger::error("Cannot register cultures until culture groups are locked!"); @@ -99,7 +99,7 @@ bool CultureManager::_load_culture( ) { colour_t colour = colour_t::null(); - std::vector<std::string> first_names, last_names; + name_list_t first_names, last_names; bool ret = expect_dictionary_keys( "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), diff --git a/src/openvic-simulation/pop/Culture.hpp b/src/openvic-simulation/pop/Culture.hpp index 1c6b75f..c8dfe7a 100644 --- a/src/openvic-simulation/pop/Culture.hpp +++ b/src/openvic-simulation/pop/Culture.hpp @@ -40,14 +40,14 @@ namespace OpenVic { private: CultureGroup const& PROPERTY(group); - const std::vector<std::string> PROPERTY(first_names); - const std::vector<std::string> PROPERTY(last_names); + const name_list_t PROPERTY(first_names); + const name_list_t PROPERTY(last_names); // TODO - radicalism, primary tag Culture( std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, - std::vector<std::string>&& new_first_names, std::vector<std::string>&& new_last_names + name_list_t&& new_first_names, name_list_t&& new_last_names ); public: @@ -75,8 +75,8 @@ namespace OpenVic { ); bool add_culture( - std::string_view identifier, colour_t colour, CultureGroup const& group, std::vector<std::string>&& first_names, - std::vector<std::string>&& last_names + std::string_view identifier, colour_t colour, CultureGroup const& group, name_list_t&& first_names, + name_list_t&& last_names ); bool load_graphical_culture_type_file(ast::NodeCPtr root); diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp index ce310e1..0c70842 100644 --- a/src/openvic-simulation/pop/Pop.cpp +++ b/src/openvic-simulation/pop/Pop.cpp @@ -194,14 +194,12 @@ bool PopManager::load_pop_type_file( "country_migration_target", ZERO_OR_ONE, country_migration_target.expect_conditional_weight(ConditionalWeight::FACTOR), "migration_target", ZERO_OR_ONE, migration_target.expect_conditional_weight(ConditionalWeight::FACTOR), "promote_to", ZERO_OR_ONE, assign_variable_callback(promote_to_node), - "ideologies", ZERO_OR_ONE, ideology_manager.expect_ideology_dictionary( + "ideologies", ZERO_OR_ONE, ideology_manager.expect_ideology_dictionary_reserve_length( + ideologies, [&filestem, &ideologies](Ideology const& ideology, ast::NodeCPtr node) -> bool { ConditionalWeight weight { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }; bool ret = weight.expect_conditional_weight(ConditionalWeight::FACTOR)(node); - if (!ideologies.emplace(&ideology, std::move(weight)).second) { - Logger::error("Duplicate ideology in pop type ", filestem, " ideology weights: ", ideology); - ret = false; - } + ret &= map_callback(ideologies, &ideology)(std::move(weight)); return ret; } ), @@ -233,7 +231,8 @@ bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_mana for (size_t index = 0; index < delayed_parse_promote_to_and_issues_nodes.size(); ++index) { const auto [promote_to_node, issues_node] = delayed_parse_promote_to_and_issues_nodes[index]; PopType* pop_type = pop_types.get_item_by_index(index); - if (promote_to_node != nullptr && !expect_pop_type_dictionary( + if (promote_to_node != nullptr && !expect_pop_type_dictionary_reserve_length( + pop_type->promote_to, [pop_type](PopType const& type, ast::NodeCPtr node) -> bool { if (pop_type == &type) { Logger::error("Pop type ", type, " cannot have promotion weight to itself!"); @@ -241,17 +240,15 @@ bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_mana } ConditionalWeight weight { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }; bool ret = weight.expect_conditional_weight(ConditionalWeight::FACTOR)(node); - if (!pop_type->promote_to.emplace(&type, std::move(weight)).second) { - Logger::error("Duplicate pop type in pop type ", pop_type, " promotion weights: ", type); - ret = false; - } + ret &= map_callback(pop_type->promote_to, &type)(std::move(weight)); return ret; } )(promote_to_node)) { Logger::error("Errors parsing pop type ", pop_type, " promotion weights!"); ret = false; } - if (issues_node != nullptr && !expect_dictionary( + if (issues_node != nullptr && !expect_dictionary_reserve_length( + pop_type->issues, [pop_type, &issue_manager](std::string_view key, ast::NodeCPtr node) -> bool { Issue const* issue = issue_manager.get_issue_by_identifier(key); if (issue == nullptr) { @@ -263,10 +260,7 @@ bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_mana } ConditionalWeight weight { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }; bool ret = weight.expect_conditional_weight(ConditionalWeight::FACTOR)(node); - if (!pop_type->issues.emplace(issue, std::move(weight)).second) { - Logger::error("Duplicate issue in pop type ", pop_type, " issue weights: ", issue->get_identifier()); - ret = false; - } + ret &= map_callback(pop_type->issues, issue)(std::move(weight)); return ret; } )(issues_node)) { diff --git a/src/openvic-simulation/scripts/Condition.cpp b/src/openvic-simulation/scripts/Condition.cpp index 73b3606..ce18824 100644 --- a/src/openvic-simulation/scripts/Condition.cpp +++ b/src/openvic-simulation/scripts/Condition.cpp @@ -721,7 +721,7 @@ node_callback_t ConditionManager::expect_condition_node_list( }; bool ret = conditions.expect_item_dictionary_and_default( - expect_node, top_scope ? top_scope_fallback : key_value_invalid_callback + top_scope ? top_scope_fallback : key_value_invalid_callback, expect_node )(node); if (!ret) { Logger::error("Error parsing condition node:\n", node); diff --git a/src/openvic-simulation/types/HasIdentifier.hpp b/src/openvic-simulation/types/HasIdentifier.hpp new file mode 100644 index 0000000..9457e75 --- /dev/null +++ b/src/openvic-simulation/types/HasIdentifier.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include <algorithm> +#include <cassert> +#include <ostream> + +#include "openvic-simulation/types/Colour.hpp" +#include "openvic-simulation/utility/Getters.hpp" + +namespace OpenVic { + constexpr bool valid_basic_identifier_char(char c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_'; + } + constexpr bool valid_basic_identifier(std::string_view identifier) { + return std::all_of(identifier.begin(), identifier.end(), valid_basic_identifier_char); + } + constexpr std::string_view extract_basic_identifier_prefix(std::string_view identifier) { + size_t len = 0; + while (len < identifier.size() && valid_basic_identifier_char(identifier[len])) { + ++len; + } + return { identifier.data(), len }; + } + + /* + * Base class for objects with a non-empty string identifier. Uniquely named instances of a type derived from this class + * can be entered into an IdentifierRegistry instance. + */ + class HasIdentifier { + const std::string PROPERTY(identifier); + + protected: + HasIdentifier(std::string_view new_identifier): identifier { new_identifier } { + assert(!identifier.empty()); + } + + public: + HasIdentifier(HasIdentifier const&) = delete; + HasIdentifier(HasIdentifier&&) = default; + HasIdentifier& operator=(HasIdentifier const&) = delete; + HasIdentifier& operator=(HasIdentifier&&) = delete; + }; + + inline std::ostream& operator<<(std::ostream& stream, HasIdentifier const& obj) { + return stream << obj.get_identifier(); + } + inline std::ostream& operator<<(std::ostream& stream, HasIdentifier const* obj) { + return obj != nullptr ? stream << *obj : stream << "<NULL>"; + } + + /* + * Base class for objects with associated colour information. + */ + template<IsColour ColourT> + class _HasColour { + const ColourT PROPERTY(colour); + + protected: + _HasColour(ColourT new_colour, bool cannot_be_null): colour { new_colour } { + assert(!cannot_be_null || !colour.is_null()); + } + + public: + _HasColour(_HasColour const&) = delete; + _HasColour(_HasColour&&) = default; + _HasColour& operator=(_HasColour const&) = delete; + _HasColour& operator=(_HasColour&&) = delete; + }; + + /* + * Base class for objects with a unique string identifier and associated colour information. + */ + template<IsColour ColourT> + class _HasIdentifierAndColour : public HasIdentifier, public _HasColour<ColourT> { + protected: + _HasIdentifierAndColour(std::string_view new_identifier, ColourT new_colour, bool cannot_be_null) + : HasIdentifier { new_identifier }, _HasColour<ColourT> { new_colour, cannot_be_null } {} + + public: + _HasIdentifierAndColour(_HasIdentifierAndColour const&) = delete; + _HasIdentifierAndColour(_HasIdentifierAndColour&&) = default; + _HasIdentifierAndColour& operator=(_HasIdentifierAndColour const&) = delete; + _HasIdentifierAndColour& operator=(_HasIdentifierAndColour&&) = delete; + }; + + using HasIdentifierAndColour = _HasIdentifierAndColour<colour_t>; + using HasIdentifierAndAlphaColour = _HasIdentifierAndColour<colour_argb_t>; +} diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp index 53a68a5..04198b0 100644 --- a/src/openvic-simulation/types/IdentifierRegistry.hpp +++ b/src/openvic-simulation/types/IdentifierRegistry.hpp @@ -1,93 +1,14 @@ #pragma once -#include <cassert> #include <vector> #include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" +#include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/utility/Getters.hpp" #include "openvic-simulation/utility/Logger.hpp" namespace OpenVic { - - constexpr bool valid_basic_identifier_char(char c) { - return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_'; - } - constexpr bool valid_basic_identifier(std::string_view identifier) { - return std::all_of(identifier.begin(), identifier.end(), valid_basic_identifier_char); - } - constexpr std::string_view extract_basic_identifier_prefix(std::string_view identifier) { - size_t len = 0; - while (len < identifier.size() && valid_basic_identifier_char(identifier[len])) { - ++len; - } - return { identifier.data(), len }; - } - - /* - * Base class for objects with a non-empty string identifier. Uniquely named instances of a type derived from this class - * can be entered into an IdentifierRegistry instance. - */ - class HasIdentifier { - const std::string PROPERTY(identifier); - - protected: - HasIdentifier(std::string_view new_identifier): identifier { new_identifier } { - assert(!identifier.empty()); - } - - public: - HasIdentifier(HasIdentifier const&) = delete; - HasIdentifier(HasIdentifier&&) = default; - HasIdentifier& operator=(HasIdentifier const&) = delete; - HasIdentifier& operator=(HasIdentifier&&) = delete; - }; - - inline std::ostream& operator<<(std::ostream& stream, HasIdentifier const& obj) { - return stream << obj.get_identifier(); - } - inline std::ostream& operator<<(std::ostream& stream, HasIdentifier const* obj) { - return obj != nullptr ? stream << *obj : stream << "<NULL>"; - } - - /* - * Base class for objects with associated colour information. - */ - template<IsColour ColourT> - class _HasColour { - const ColourT PROPERTY(colour); - - protected: - _HasColour(ColourT new_colour, bool cannot_be_null): colour { new_colour } { - assert(!cannot_be_null || !colour.is_null()); - } - - public: - _HasColour(_HasColour const&) = delete; - _HasColour(_HasColour&&) = default; - _HasColour& operator=(_HasColour const&) = delete; - _HasColour& operator=(_HasColour&&) = delete; - }; - - /* - * Base class for objects with a unique string identifier and associated colour information. - */ - template<IsColour ColourT> - class _HasIdentifierAndColour : public HasIdentifier, public _HasColour<ColourT> { - protected: - _HasIdentifierAndColour(std::string_view new_identifier, ColourT new_colour, bool cannot_be_null) - : HasIdentifier { new_identifier }, _HasColour<ColourT> { new_colour, cannot_be_null } {} - - public: - _HasIdentifierAndColour(_HasIdentifierAndColour const&) = delete; - _HasIdentifierAndColour(_HasIdentifierAndColour&&) = default; - _HasIdentifierAndColour& operator=(_HasIdentifierAndColour const&) = delete; - _HasIdentifierAndColour& operator=(_HasIdentifierAndColour&&) = delete; - }; - - using HasIdentifierAndColour = _HasIdentifierAndColour<colour_t>; - using HasIdentifierAndAlphaColour = _HasIdentifierAndColour<colour_argb_t>; - /* Callbacks for trying to add duplicate keys via UniqueKeyRegistry::add_item */ static bool duplicate_fail_callback(std::string_view registry_name, std::string_view duplicate_identifier) { Logger::error( @@ -199,9 +120,17 @@ namespace OpenVic { Logger::error("Failed to reserve space for ", size, " items in ", name, " registry - already locked!"); } else { items.reserve(size); + identifier_index_map.reserve(size); } } + static NodeTools::KeyValueCallback auto key_value_invalid_callback(std::string_view name) { + return [name](std::string_view key, ast::NodeCPtr) { + Logger::error("Invalid ", name, ": ", key); + return false; + }; + } + #define GETTERS(CONST) \ value_type CONST* get_item_by_identifier(std::string_view identifier) CONST { \ const typename decltype(identifier_index_map)::const_iterator it = identifier_index_map.find(identifier); \ @@ -235,12 +164,12 @@ namespace OpenVic { ) CONST { \ return NodeTools::expect_identifier(expect_item_str(callback, warn)); \ } \ - NodeTools::NodeCallback auto expect_item_dictionary_and_default( \ - NodeTools::Callback<value_type CONST&, ast::NodeCPtr> auto callback, \ - NodeTools::KeyValueCallback auto default_callback \ + NodeTools::NodeCallback auto expect_item_assign_and_default( \ + NodeTools::KeyValueCallback auto default_callback, \ + NodeTools::Callback<value_type CONST&, ast::NodeCPtr> auto callback \ ) CONST { \ - return NodeTools::expect_dictionary( \ - [this, callback, default_callback](std::string_view key, ast::NodeCPtr value) -> bool { \ + return NodeTools::expect_assign( \ + [this, default_callback, callback](std::string_view key, ast::NodeCPtr value) -> bool { \ value_type CONST* item = get_item_by_identifier(key); \ if (item != nullptr) { \ return callback(*item, value); \ @@ -250,14 +179,70 @@ namespace OpenVic { } \ ); \ } \ + NodeTools::NodeCallback auto expect_item_assign( \ + NodeTools::Callback<value_type CONST&, ast::NodeCPtr> auto callback \ + ) CONST { \ + return expect_item_assign_and_default(key_value_invalid_callback(name), callback); \ + } \ + NodeTools::NodeCallback auto expect_item_dictionary_and_length_and_default( \ + NodeTools::LengthCallback auto length_callback, \ + NodeTools::KeyValueCallback auto default_callback, \ + NodeTools::Callback<value_type CONST&, ast::NodeCPtr> auto callback \ + ) CONST { \ + return NodeTools::expect_list_and_length( \ + length_callback, expect_item_assign_and_default(default_callback, callback) \ + ); \ + } \ + NodeTools::NodeCallback auto expect_item_dictionary_and_length( \ + NodeTools::LengthCallback auto length_callback, \ + NodeTools::Callback<value_type CONST&, ast::NodeCPtr> auto callback \ + ) CONST { \ + return expect_item_dictionary_and_length_and_default( \ + length_callback, \ + key_value_invalid_callback(name), \ + callback \ + ); \ + } \ + NodeTools::NodeCallback auto expect_item_dictionary_and_default( \ + NodeTools::KeyValueCallback auto default_callback, \ + NodeTools::Callback<value_type CONST&, ast::NodeCPtr> auto callback \ + ) CONST { \ + return expect_item_dictionary_and_length_and_default( \ + NodeTools::default_length_callback, \ + default_callback, \ + callback \ + ); \ + } \ NodeTools::NodeCallback auto expect_item_dictionary( \ NodeTools::Callback<value_type CONST&, ast::NodeCPtr> auto callback \ ) CONST { \ - return expect_item_dictionary_and_default( \ - callback, [this](std::string_view key, ast::NodeCPtr) -> bool { \ - Logger::error("Invalid ", name, ": ", key); \ - return false; \ - } \ + return expect_item_dictionary_and_length_and_default( \ + NodeTools::default_length_callback, \ + key_value_invalid_callback(name), \ + callback \ + ); \ + } \ + template<NodeTools::Reservable T> \ + NodeTools::NodeCallback auto expect_item_dictionary_reserve_length_and_default( \ + T& t, \ + NodeTools::KeyValueCallback auto default_callback, \ + NodeTools::Callback<value_type CONST&, ast::NodeCPtr> auto callback \ + ) CONST { \ + return expect_item_dictionary_and_length_and_default( \ + NodeTools::reserve_length_callback(t), \ + default_callback, \ + callback \ + ); \ + } \ + template<NodeTools::Reservable T> \ + NodeTools::NodeCallback auto expect_item_dictionary_reserve_length( \ + T& t, \ + NodeTools::Callback<value_type CONST&, ast::NodeCPtr> auto callback \ + ) CONST { \ + return expect_item_dictionary_and_length_and_default( \ + NodeTools::reserve_length_callback(t), \ + key_value_invalid_callback(name), \ + callback \ ); \ } @@ -432,15 +417,48 @@ private: ) const_kw { \ return registry.expect_item_identifier(callback, warn); \ } \ + NodeTools::NodeCallback auto expect_##singular##_assign_and_default( \ + NodeTools::KeyValueCallback auto default_callback, \ + NodeTools::Callback<decltype(registry)::value_type const_kw&, ast::NodeCPtr> auto callback \ + ) const_kw { \ + return registry.expect_item_assign_and_default(default_callback, callback); \ + } \ + NodeTools::NodeCallback auto expect_##singular##_assign( \ + NodeTools::Callback<decltype(registry)::value_type const_kw&, ast::NodeCPtr> auto callback \ + ) const_kw { \ + return registry.expect_item_assign(callback); \ + } \ + NodeTools::NodeCallback auto expect_##singular##_dictionary_and_length_and_default( \ + NodeTools::LengthCallback auto length_callback, \ + NodeTools::KeyValueCallback auto default_callback, \ + NodeTools::Callback<decltype(registry)::value_type const_kw&, ast::NodeCPtr> auto callback \ + ) const_kw { \ + return registry.expect_item_dictionary_and_length_and_default(length_callback, default_callback, callback); \ + } \ NodeTools::NodeCallback auto expect_##singular##_dictionary_and_default( \ - NodeTools::Callback<decltype(registry)::value_type const_kw&, ast::NodeCPtr> auto callback, \ - NodeTools::KeyValueCallback auto default_callback \ + NodeTools::KeyValueCallback auto default_callback, \ + NodeTools::Callback<decltype(registry)::value_type const_kw&, ast::NodeCPtr> auto callback \ ) const_kw { \ - return registry.expect_item_dictionary_and_default(callback, default_callback); \ + return registry.expect_item_dictionary_and_default(default_callback, callback); \ } \ NodeTools::NodeCallback auto expect_##singular##_dictionary( \ NodeTools::Callback<decltype(registry)::value_type const_kw&, ast::NodeCPtr> auto callback \ ) const_kw { \ return registry.expect_item_dictionary(callback); \ + } \ + template<NodeTools::Reservable T> \ + NodeTools::NodeCallback auto expect_##singular##_dictionary_reserve_length_and_default( \ + T& t, \ + NodeTools::KeyValueCallback auto default_callback, \ + NodeTools::Callback<decltype(registry)::value_type const_kw&, ast::NodeCPtr> auto callback \ + ) const_kw { \ + return registry.expect_item_dictionary_reserve_length_and_default(t, default_callback, callback); \ + } \ + template<NodeTools::Reservable T> \ + NodeTools::NodeCallback auto expect_##singular##_dictionary_reserve_length( \ + T& t, \ + NodeTools::Callback<decltype(registry)::value_type const_kw&, ast::NodeCPtr> auto callback \ + ) const_kw { \ + return registry.expect_item_dictionary_reserve_length(t, callback); \ } } diff --git a/src/openvic-simulation/types/OrderedContainers.hpp b/src/openvic-simulation/types/OrderedContainers.hpp index 1df9b10..e9f8717 100644 --- a/src/openvic-simulation/types/OrderedContainers.hpp +++ b/src/openvic-simulation/types/OrderedContainers.hpp @@ -146,7 +146,7 @@ namespace OpenVic { template<class Key, class T, class Allocator = std::allocator<std::pair<Key, T>>, class IndexType = std::uint_least32_t> using case_insensitive_deque_ordered_map = deque_ordered_map<Key, T, case_insensitive_string_hash, case_insensitive_string_equal, Allocator, IndexType>; - + template<class Key, class T, class Allocator = std::allocator<std::pair<Key, T>>, class IndexType = std::uint_least32_t> using case_insensitive_ordered_map = case_insensitive_vector_ordered_map<Key, T, Allocator, IndexType>; |