diff --git a/src/ai/lua/lua_object.cpp b/src/ai/lua/lua_object.cpp index c4ec5b6d8029..346857c9b9fc 100644 --- a/src/ai/lua/lua_object.cpp +++ b/src/ai/lua/lua_object.cpp @@ -47,8 +47,7 @@ namespace ai { } lua_getfield(L, n, "own"); if(lua_istable(L, -1)) { - config cfg; - vconfig vcfg(cfg, true); + vconfig vcfg(config(), true); if(luaW_tovconfig(L, -1, vcfg)) { att->filter_own_.reset(new unit_filter(vcfg, resources::filter_con)); } @@ -59,8 +58,7 @@ namespace ai { } lua_getfield(L, n, "enemy"); if(lua_istable(L, -1)) { - config cfg; - vconfig vcfg(cfg, true); + vconfig vcfg(config(), true); if(luaW_tovconfig(L, -1, vcfg)) { att->filter_enemy_.reset(new unit_filter(vcfg, resources::filter_con)); } diff --git a/src/scripting/lua_common.cpp b/src/scripting/lua_common.cpp index 2bcf517fb2fc..720701e39c93 100644 --- a/src/scripting/lua_common.cpp +++ b/src/scripting/lua_common.cpp @@ -799,7 +799,7 @@ bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg) config cfg; bool ok = luaW_toconfig(L, index, cfg); if (!ok) return false; - vcfg = vconfig(cfg, true); + vcfg = vconfig(std::move(cfg)); break; } case LUA_TUSERDATA: diff --git a/src/side_filter.cpp b/src/side_filter.cpp index 5955b70ef747..9a484b88e659 100644 --- a/src/side_filter.cpp +++ b/src/side_filter.cpp @@ -123,8 +123,10 @@ bool side_filter::match_internal(const team &t) const //Allow filtering on units if(cfg_.has_child("has_unit")) { const vconfig & ufilt_cfg = cfg_.child("has_unit"); - if (!ufilter_) - ufilter_.reset(new unit_filter(ufilt_cfg, fc_, flat_)); + if (!ufilter_) { + ufilter_.reset(new unit_filter(ufilt_cfg.make_safe(), fc_)); + ufilter_->set_use_flat_tod(flat_); + } bool found = false; for(const unit &u : fc_->get_disp_context().units()) { if (u.side() != t.side()) { diff --git a/src/terrain/filter.cpp b/src/terrain/filter.cpp index 988f492d3487..7524b2bfa62b 100644 --- a/src/terrain/filter.cpp +++ b/src/terrain/filter.cpp @@ -165,8 +165,10 @@ bool terrain_filter::match_internal(const map_location& loc, const unit* ref_uni const unit_map::const_iterator u = fc_->get_disp_context().units().find(loc); if (!u.valid()) return false; - if (!cache_.ufilter_) - cache_.ufilter_.reset(new unit_filter(cfg_.child("filter"), fc_, flat_)); + if (!cache_.ufilter_) { + cache_.ufilter_.reset(new unit_filter(cfg_.child("filter").make_safe(), fc_)); + cache_.ufilter_->set_use_flat_tod(flat_); + } if (!cache_.ufilter_->matches(*u, loc)) return false; } diff --git a/src/units/abilities.cpp b/src/units/abilities.cpp index 2aebd4ab2dba..7325bce4b069 100644 --- a/src/units/abilities.cpp +++ b/src/units/abilities.cpp @@ -306,7 +306,7 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map bool illuminates = ability == "illuminates"; if (const config &afilter = cfg.child("filter")) - if ( !unit_filter(vconfig(afilter), resources::filter_con, illuminates).matches(*this, loc) ) + if ( !unit_filter(vconfig(afilter), resources::filter_con).set_use_flat_tod(illuminates).matches(*this, loc) ) return false; map_location adjacent[6]; @@ -318,7 +318,8 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map for (const config &i : cfg.child_range("filter_adjacent")) { size_t count = 0; - const unit_filter ufilt(vconfig(i), resources::filter_con, illuminates); + unit_filter ufilt(vconfig(i), resources::filter_con); + ufilt.set_use_flat_tod(illuminates); std::vector dirs = map_location::parse_directions(i["adjacent"]); for (const map_location::DIRECTION index : dirs) { @@ -389,7 +390,7 @@ bool unit::ability_affects_adjacent(const std::string& ability, const config& cf } const config &filter = i.child("filter"); if (!filter || //filter tag given - unit_filter(vconfig(filter), resources::filter_con, illuminates).matches(*this, loc, from) ) { + unit_filter(vconfig(filter), resources::filter_con).set_use_flat_tod(illuminates).matches(*this, loc, from) ) { return true; } } @@ -401,7 +402,7 @@ bool unit::ability_affects_self(const std::string& ability,const config& cfg,con const config &filter = cfg.child("filter_self"); bool affect_self = cfg["affect_self"].to_bool(true); if (!filter || !affect_self) return affect_self; - return unit_filter(vconfig(filter), resources::filter_con, ability == "illuminates").matches(*this, loc); + return unit_filter(vconfig(filter), resources::filter_con).set_use_flat_tod(ability == "illuminates").matches(*this, loc); } bool unit::has_ability_type(const std::string& ability) const diff --git a/src/units/filter.cpp b/src/units/filter.cpp index 51dd651f0aff..0a64b6b57752 100644 --- a/src/units/filter.cpp +++ b/src/units/filter.cpp @@ -17,10 +17,7 @@ #include "log.hpp" #include "config.hpp" -#include "display_context.hpp" -#include "filter_context.hpp" #include "game_data.hpp" -#include "utils/make_enum.hpp" #include "map/map.hpp" #include "map/location.hpp" #include "scripting/game_lua_kernel.hpp" //Needed for lua kernel @@ -30,718 +27,662 @@ #include "tod_manager.hpp" #include "units/unit.hpp" #include "units/formula_manager.hpp" -#include "units/map.hpp" #include "units/types.hpp" #include "variable.hpp" // needed for vconfig, scoped unit #include "wml_exception.hpp" // needed for FAIL #include "formula/callable_objects.hpp" #include "formula/formula.hpp" #include "formula/function_gamestate.hpp" +#include "formula/string_utils.hpp" +#include "resources.hpp" #include -#include - static lg::log_domain log_config("config"); #define ERR_CF LOG_STREAM(err, log_config) #define DBG_CF LOG_STREAM(debug, log_config) -// Defined out of line to avoid including config in unit_filter.hpp -config unit_filter::to_config() const { - return impl_->to_config(); -} +using namespace unit_filter_impl; -///Defined out of line to prevent including unit at unit_filter.hpp -bool unit_filter::matches(const unit & u) const { - return matches (u, u.get_location()); -} - -bool unit_filter::matches(const unit & u, const map_location & loc, const unit & u2) const { - return impl_->matches(u,loc,&u2); -} +namespace { -bool unit_filter::matches(const unit & u, const unit & u2) const { - return matches(u, u.get_location(), u2); -} +struct unit_filter_xy : public unit_filter_base +{ + unit_filter_xy(const std::string& x, const std::string& y) : x_(x), y_(y) {} -//bool unit_filter::matches(const unit & /*u*/, const map_location & /*loc*/) const { -// assert(false && "called match against a pure abstract unit_filter! this indicates a programmer error, this function must be overrided"); -// return false; -//} + virtual bool matches(const unit_filter_args& args) const override + { + std::string x = utils::interpolate_variables_into_string(x_, *(resources::gamedata)); + std::string y = utils::interpolate_variables_into_string(y_, *(resources::gamedata)); + if(x == "recall" && y == "recall") { + return !args.fc->get_disp_context().map().on_board(args.loc); + } + else { + return args.loc.matches_range(x, y); + } + } -/// Forward declare the "construct" method which constructs an appropriate filter impl -static std::shared_ptr construct(const vconfig & vcfg, const filter_context & fc, bool flat_tod); + const std::string x_; + const std::string y_; +}; -/// Null unit filter is built when the input config is null -class null_unit_filter_impl : public unit_filter_abstract_impl { -public: - null_unit_filter_impl(const filter_context & fc) : fc_(fc) {} - virtual bool matches(const unit & /*u*/, const map_location & /*loc*/, const unit *) const { - return true; +struct unit_filter_adjacent : public unit_filter_base +{ + unit_filter_adjacent(const vconfig& cfg) + : child_(cfg) + , cfg_(cfg) + { } - virtual std::vector all_matches_on_map(unsigned max_matches, const map_location* = nullptr, const unit* = nullptr) const { - std::vector ret; - for(const unit & u : fc_.get_disp_context().units()) { - --max_matches; - ret.push_back(&u); - if(max_matches == 0) { - return ret; + + virtual bool matches(const unit_filter_args& args) const override + { + const unit_map& units = args.fc->get_disp_context().units(); + map_location adjacent[6]; + get_adjacent_tiles(args.loc, adjacent); + int match_count=0; + + config::attribute_value i_adjacent = cfg_["adjacent"]; + std::vector dirs; + if (i_adjacent.empty()) { + dirs = map_location::default_dirs(); + } else { + dirs = map_location::parse_directions(i_adjacent); + } + for (map_location::DIRECTION dir : dirs) { + unit_map::const_iterator unit_itor = units.find(adjacent[dir]); + if (unit_itor == units.end() || !child_.matches(unit_filter_args{*unit_itor, unit_itor->get_location(), &args.u, args.fc, args.use_flat_tod} )) { + continue; } + auto is_enemy = cfg_["is_enemy"]; + if (!is_enemy.empty() && is_enemy.to_bool() != args.fc->get_disp_context().get_team(args.u.side()).is_enemy(unit_itor->side())) { + continue; + } + ++match_count; } - return ret; - } - virtual unit_const_ptr first_match_on_map() const { - return fc_.get_disp_context().units().begin().get_shared_ptr(); + static std::vector > default_counts = utils::parse_ranges("1-6"); + config::attribute_value i_count = cfg_["count"]; + return in_ranges(match_count, !i_count.blank() ? utils::parse_ranges(i_count) : default_counts); } + const unit_filter_compound child_; + const vconfig cfg_; +}; - virtual ~null_unit_filter_impl() {} - config to_config() const { - return config(); +template +struct unit_filter_child_literal : public unit_filter_base +{ + unit_filter_child_literal(const vconfig& v, const F& f) : v_(v) , f_(f) {} + virtual bool matches(const unit_filter_args& args) const override + { + return f_(v_, args); } + vconfig v_; + F f_; +}; - bool empty() const { - return true; +template +struct unit_filter_attribute_parsed : public unit_filter_base +{ + unit_filter_attribute_parsed(T&& v, F&& f) : v_(std::move(v)), f_(std::move(f)) {} + virtual bool matches(const unit_filter_args& args) const override + { + return f_(v_, args); } - -private: - const filter_context & fc_; + T v_; + F f_; }; -/// This enum helps to evaluate conditional filters -namespace conditional { - MAKE_ENUM (TYPE, - (AND, "and") - (OR, "or") - (NOT, "not") - ) -} - - -/// The basic unit filter gives a generic implementation of the match fcn -class basic_unit_filter_impl : public unit_filter_abstract_impl { -public: - basic_unit_filter_impl(const vconfig & vcfg, const filter_context & fc, bool flat_tod) - : fc_(fc) - , vcfg(vcfg) - , use_flat_tod_(flat_tod) - , cond_children_() - , cond_child_types_() +template +struct unit_filter_attribute_literal : public unit_filter_base +{ + unit_filter_attribute_literal(std::string&& v, C&& c, F&& f) : v_(std::move(v)), c_(std::move(c)), f_(std::move(f)) {} + virtual bool matches(const unit_filter_args& args) const override { - // Handle [and], [or], and [not] with in-order precedence - vconfig::all_children_iterator cond = vcfg.ordered_begin(); - vconfig::all_children_iterator cond_end = vcfg.ordered_end(); - while(cond != cond_end) - { - const std::string& cond_name = cond.get_key(); - conditional::TYPE type; - if(type.parse(cond_name)) { - const vconfig& cond_filter = cond.get_child(); - - cond_children_.emplace_back(cond_filter, &fc_, use_flat_tod_); - cond_child_types_.push_back(type); - } - else { - static const int NUM_VALID_TAGS = 5; - static const std::string valid_tags[NUM_VALID_TAGS] { - "filter_vision", - "filter_adjacent", - "filter_location", - "filter_side", - "filter_wml", - }; - static const std::string* const valid_tags_end = valid_tags + NUM_VALID_TAGS; - - if (std::find(valid_tags, valid_tags_end, cond_name) == valid_tags_end){ - std::stringstream errmsg; - errmsg << "encountered a child [" << cond_name << "] of a standard unit filter, it is being ignored"; - DBG_CF << errmsg.str() << std::endl; //FAIL( errmsg.str() ); - } - - } - ++cond; - } - this->vcfg.make_safe(); + config::attribute_value v; + v = utils::interpolate_variables_into_string(v_, *(resources::gamedata)); + return f_(c_(v), args); } + std::string v_; + C c_; + F f_; +}; - virtual bool matches(const unit & u, const map_location & loc, const unit * u2) const; - virtual std::vector all_matches_on_map(unsigned max_matches, const map_location* loc = nullptr, const unit* u2 = nullptr) const; - virtual unit_const_ptr first_match_on_map() const; - config to_config() const { - return vcfg.get_config(); - } +class contains_dollar_visitor : public boost::static_visitor +{ +public: + contains_dollar_visitor() {} - virtual ~basic_unit_filter_impl() {} -private: - const filter_context & fc_; - const vconfig vcfg; - bool use_flat_tod_; + + template + bool operator()(T const &) const { return false; } - std::vector cond_children_; - std::vector cond_child_types_; + bool operator()(t_string const&) const { return true; } - bool internal_matches_filter(const unit & u, const map_location & loc, const unit* u2) const; + bool operator()(const std::string& s) const + { + return s.find('$') != std::string::npos; + } }; -/** "Factory" method which constructs an appropriate implementation - * - */ - -static std::shared_ptr construct(const vconfig & vcfg, const filter_context & fc, bool flat_tod) -{ - if (vcfg.empty()) { - return std::make_shared (fc); - } - if (vcfg.get_config().attribute_count() == 1 && vcfg.get_config().all_children_count() == 0 && vcfg.has_attribute("limit")) { - return std::make_shared (fc); - } - return std::make_shared(vcfg, fc, flat_tod); - //TODO: Add more efficient implementations for special cases } -/** Ctor of unit filter - * unit_filter::unit_filter acts as a factory, selecting the appropriate implementation class - */ -unit_filter::unit_filter(const vconfig & vcfg, const filter_context * fc, bool flat_tod) - : impl_() - , max_matches_(static_cast(-1)) + +unit_filter_compound::unit_filter_compound(vconfig cfg) + : children_() + , cond_children_() { - if(vcfg) { - max_matches_ = vcfg["limit"].to_unsigned(max_matches_); - } - if (!fc) { - assert(false && "attempt to instantiate a unit filter with a null filter context!"); - } - impl_ = construct(vcfg, *fc, flat_tod); + fill(cfg); } -/** Begin implementations of filter impl's - */ - -bool basic_unit_filter_impl::matches(const unit & u, const map_location& loc, const unit * u2) const +bool unit_filter_compound::matches(const unit_filter_args& args) const { - bool matches = true; - - if(loc.valid()) { - scoped_xy_unit auto_store("this_unit", loc, fc_.get_disp_context().units()); - if (u2) { - const map_location& loc2 = u2->get_location(); - scoped_xy_unit u2_auto_store("other_unit", loc2, fc_.get_disp_context().units()); - matches = internal_matches_filter(u, loc, u2); + bool res; + + if(args.loc.valid()) { + scoped_xy_unit auto_store("this_unit", args.loc, args.fc->get_disp_context().units()); + if (args.u2) { + const map_location& loc2 = args.u2->get_location(); + scoped_xy_unit u2_auto_store("other_unit", loc2, args.fc->get_disp_context().units()); + res = filter_impl(args); } else { - matches = internal_matches_filter(u, loc, u2); + res = filter_impl(args); } } else { // If loc is invalid, then this is a recall list unit (already been scoped) - matches = internal_matches_filter(u, loc, nullptr); + res = filter_impl(args); } // Handle [and], [or], and [not] with in-order precedence - for (size_t i = 0; i < cond_children_.size(); i++) { - switch (cond_child_types_[i].v) { - case conditional::TYPE::AND: - if(u2) { - matches = matches && cond_children_[i].matches(u,loc,*u2); - } else { - matches = matches && cond_children_[i].matches(u,loc); - } - break; - case conditional::TYPE::OR: - if(u2) { - matches = matches || cond_children_[i].matches(u,loc,*u2); - } else { - matches = matches || cond_children_[i].matches(u,loc); - } - break; - case conditional::TYPE::NOT: - if(u2) { - matches = matches && !cond_children_[i].matches(u,loc,*u2); - } else { - matches = matches && !cond_children_[i].matches(u,loc); - } - } - } - return matches; + for(const auto & filter : cond_children_) { + bool child_res = filter.second.matches(args); + + switch (filter.first.v) { + case CONDITIONAL_TYPE::AND: + res = res && child_res; + break; + case CONDITIONAL_TYPE::OR: + res = res || child_res; + break; + case CONDITIONAL_TYPE::NOT: + res = res && !child_res; + break; + } + } + return res; } -bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_location& loc, const unit* u2) const +bool unit_filter_compound::filter_impl(const unit_filter_args& args) const { - if (!vcfg["name"].blank() && vcfg["name"].t_str() != u.name()) { - return false; - } - - if (!vcfg["id"].blank()) { - std::vector id_list = utils::split(vcfg["id"]); - if (std::find(id_list.begin(), id_list.end(), u.id()) == id_list.end()) { - return false; - } - } - - // Allow 'speaker' as an alternative to id, since people use it so often - if (!vcfg["speaker"].blank() && vcfg["speaker"].str() != u.id()) { - return false; - } - - if (vcfg.has_child("filter_location")) { - if (vcfg.count_children("filter_location") > 1) { - FAIL("Encountered multiple [filter_location] children of a standard unit filter. " - "This is not currently supported and in all versions of wesnoth would have " - "resulted in the later children being ignored. You must use [and] or similar " - "to achieve the desired result."); - } - terrain_filter filt(vcfg.child("filter_location"), &fc_, use_flat_tod_); - if (!filt.match(loc)) { + for(const auto & filter : children_) { + if (!filter->matches(args)) { return false; } } + return true; +} - if(vcfg.has_child("filter_side")) { - if (vcfg.count_children("filter_side") > 1) { - FAIL("Encountered multiple [filter_side] children of a standard unit filter. " - "This is not currently supported and in all versions of wesnoth would have " - "resulted in the later children being ignored. You must use [and] or similar " - "to achieve the desired result."); - } - side_filter filt(vcfg.child("filter_side"), &fc_); - if(!filt.match(u.side())) - return false; - } +template +void unit_filter_compound::create_child(const vconfig& c, F func) +{ + children_.emplace_back(new unit_filter_child_literal(c, func)); +} - // Also allow filtering on location ranges outside of the location filter - if (!vcfg["x"].blank() || !vcfg["y"].blank()){ - if(vcfg["x"] == "recall" && vcfg["y"] == "recall") { - //locations on the map are considered to not be on a recall list - if (fc_.get_disp_context().map().on_board(loc)) - { - return false; - } - } else if(vcfg["x"].empty() && vcfg["y"].empty()) { - return false; - } else if(!loc.matches_range(vcfg["x"], vcfg["y"])) { - return false; - } +template +void unit_filter_compound::create_attribute(const config::attribute_value v, C conv, F func) +{ + if(v.empty()) { } - - // The type could be a comma separated list of types - if (!vcfg["type"].empty()) { - std::vector types = utils::split(vcfg["type"]); - if (std::find(types.begin(), types.end(), u.type_id()) == types.end()) { - return false; - } + else if(v.apply_visitor(contains_dollar_visitor())) { + children_.emplace_back(new unit_filter_attribute_literal(std::move(v.str()), std::move(conv), std::move(func))); } - - // Shorthand for all advancements of a given type - if (!vcfg["type_adv_tree"].empty()) { - std::set types; - for(const std::string type : utils::split(vcfg["type_adv_tree"])) { - if(types.count(type)) { - continue; - } - if(const unit_type* ut = unit_types.find(type)) { - const auto& tree = ut->advancement_tree(); - types.insert(tree.begin(), tree.end()); - types.insert(type); - } - } - if(types.find(u.type_id()) == types.end()) { - return false; - } + else { + children_.emplace_back(new unit_filter_attribute_parsed(std::move(conv(v)), std::move(func))); } +} - // The variation_type could be a comma separated list of types - if (!vcfg["variation"].empty()) +void unit_filter_compound::fill(vconfig cfg) { - std::vector types = utils::split(vcfg["variation"]); - if (std::find(types.begin(), types.end(), u.variation()) == types.end()) { - return false; - } - } + const config& literal = cfg.get_config(); - // The has_variation_type could be a comma separated list of types - if (!vcfg["has_variation"].empty()) - { - bool match = false; - // If this unit is a variation itself then search in the base unit's variations. - const unit_type* const type = u.variation().empty() ? &u.type() : unit_types.find(u.type().base_id()); - assert(type); + //optimisation + if(literal.empty()) { return; } - for (const std::string& variation_id : utils::split(vcfg["has_variation"])) { - if (type->has_variation(variation_id)) { - match = true; - break; - } - } - if (!match) return false; - } - - if (!vcfg["ability"].empty()) - { - bool match = false; + create_attribute(literal["name"], + [](const config::attribute_value& c) { return c.t_str(); }, + [](const t_string& str, const unit_filter_args& args) { return str == args.u.name(); } + ); - for (const std::string& ability_id : utils::split(vcfg["ability"])) { - if (u.has_ability_by_id(ability_id)) { - match = true; - break; + create_attribute(literal["id"], + [](const config::attribute_value& c) { return utils::split(c.str()); }, + [](const std::vector& id_list, const unit_filter_args& args) + { + return std::find(id_list.begin(), id_list.end(), args.u.id()) != id_list.end(); } - } - if (!match) return false; - } - - if (!vcfg["ability_type"].empty()) - { - bool match = false; + ); - for (const std::string& ability : utils::split(vcfg["ability_type"])) { - if (u.has_ability_type(ability)) { - match = true; - break; + create_attribute(literal["speaker"], + [](const config::attribute_value& c) { return c.str(); }, + [](const std::string& speaker, const unit_filter_args& args) + { + return speaker == args.u.id(); } - } - if (!match) return false; - } + ); - if(!vcfg["ability_type_active"].empty()) { - bool match = false; - - for(const std::string& ability : utils::split(vcfg["ability_type_active"])) { - if(!u.get_abilities(ability, loc).empty()) { - match = true; - break; + create_attribute(literal["type"], + [](const config::attribute_value& c) { return utils::split(c.str()); }, + [](const std::vector& types, const unit_filter_args& args) + { + return std::find(types.begin(), types.end(), args.u.type_id()) != types.end(); } - } - if(!match) { - return false; - } - } + ); - if(!vcfg["trait"].empty()) { - std::vector check_traits = utils::split(vcfg["trait"]), have_traits = u.get_traits_list(), isect; - std::sort(check_traits.begin(), check_traits.end()); - std::sort(have_traits.begin(), have_traits.end()); - std::set_intersection(check_traits.begin(), check_traits.end(), have_traits.begin(), have_traits.end(), std::back_inserter(isect)); - if(isect.empty()) { - return false; - } - } - - if (!vcfg["race"].empty()) { - std::vector races = utils::split(vcfg["race"]); - if (std::find(races.begin(), races.end(), u.race()->id()) == races.end()) { - return false; - } - } - - if (!vcfg["gender"].blank() && string_gender(vcfg["gender"]) != u.gender()) { - return false; - } - - if (!vcfg["side"].empty() && vcfg["side"].to_int(-999) != u.side()) { - std::vector sides = utils::split(vcfg["side"]); - const std::string u_side = std::to_string(u.side()); - if (std::find(sides.begin(), sides.end(), u_side) == sides.end()) { - return false; - } - } - - // handle statuses list - if (!vcfg["status"].empty()) { - bool status_found = false; + create_attribute(literal["type_adv_tree"], + [](const config::attribute_value& c) { return utils::split(c.str()); }, + [](const std::vector& types, const unit_filter_args& args) + { + std::set types_expanded; + for(const std::string& type : types) { + if(types_expanded.count(type)) { + continue; + } + if(const unit_type* ut = unit_types.find(type)) { + const auto& tree = ut->advancement_tree(); + types_expanded.insert(tree.begin(), tree.end()); + types_expanded.insert(type); + } + } + return types_expanded.find(args.u.type_id()) != types_expanded.end(); + } + ); - for (const std::string status : utils::split(vcfg["status"])) { - if(u.get_state(status)) { - status_found = true; - break; + create_attribute(literal["variation"], + [](const config::attribute_value& c) { return utils::split(c.str()); }, + [](const std::vector& types, const unit_filter_args& args) + { + return std::find(types.begin(), types.end(), args.u.variation()) != types.end(); } - } + ); - if(!status_found) { - return false; - } - } + create_attribute(literal["has_variation"], + [](const config::attribute_value& c) { return utils::split(c.str()); }, + [](const std::vector& types, const unit_filter_args& args) + { + // If this unit is a variation itself then search in the base unit's variations. + const unit_type* const type = args.u.variation().empty() ? &args.u.type() : unit_types.find(args.u.type().base_id()); + assert(type); + + for(const std::string& variation_id : types) { + if (type->has_variation(variation_id)) { + return true; + } + } + return false; + } + ); - if (vcfg.has_child("has_attack")) { - const vconfig& weap_filter = vcfg.child("has_attack"); - bool has_weapon = false; - for(const attack_type& a : u.attacks()) { - if(a.matches_filter(weap_filter.get_parsed_config())) { - has_weapon = true; - break; + create_attribute(literal["ability"], + [](const config::attribute_value& c) { return utils::split(c.str()); }, + [](const std::vector& abilities, const unit_filter_args& args) + { + for(const std::string& ability_id : abilities) { + if (args.u.has_ability_by_id(ability_id)) { + return true; + } + } + return false; } - } - if(!has_weapon) { - return false; - } - } else if (!vcfg["has_weapon"].blank()) { - std::string weapon = vcfg["has_weapon"]; - bool has_weapon = false; - for(const attack_type& a : u.attacks()) { - if(a.id() == weapon) { - has_weapon = true; - break; + ); + + create_attribute(literal["ability_type"], + [](const config::attribute_value& c) { return utils::split(c.str()); }, + [](const std::vector& abilities, const unit_filter_args& args) + { + for(const std::string& ability : abilities) { + if (args.u.has_ability_type(ability)) { + return true; + } + } + return false; } - } - if(!has_weapon) { - return false; - } - } + ); - if (!vcfg["role"].blank() && vcfg["role"].str() != u.get_role()) { - return false; - } + create_attribute(literal["ability_type_active"], + [](const config::attribute_value& c) { return utils::split(c.str()); }, + [](const std::vector& abilities, const unit_filter_args& args) + { + for(const std::string& ability : abilities) { + if (!args.u.get_abilities(ability, args.loc).empty()) { + return true; + } + } + return false; + } + ); - if (!vcfg["ai_special"].blank() && ((vcfg["ai_special"].str() == "guardian") != u.get_state(unit::STATE_GUARDIAN))) { - return false; - } + create_attribute(literal["trait"], + [](const config::attribute_value& c) + { + auto res = utils::split(c.str()); + std::sort(res.begin(), res.end()); + return res; - if (!vcfg["canrecruit"].blank() && vcfg["canrecruit"].to_bool() != u.can_recruit()) { - return false; - } + }, + [](const std::vector& check_traits, const unit_filter_args& args) + { - if (!vcfg["recall_cost"].blank()) { - bool match_found = false; - for(auto cost : utils::parse_ranges(vcfg["recall_cost"])) { - if(cost.first <= u.recall_cost() && u.recall_cost() <= cost.second) { - match_found = true; - break; + std::vector have_traits = args.u.get_traits_list(); + std::vector isect; + std::sort(have_traits.begin(), have_traits.end()); + std::set_intersection(check_traits.begin(), check_traits.end(), have_traits.begin(), have_traits.end(), std::back_inserter(isect)); + return !isect.empty(); } - } - if(!match_found) { - return false; - } - } + ); - if(!vcfg["level"].blank()) { - bool match_found = false; - for(auto lvl : utils::parse_ranges(vcfg["level"])) { - if(lvl.first <= u.level() && u.level() <= lvl.second) { - match_found = true; - break; + create_attribute(literal["race"], + [](const config::attribute_value& c) { return utils::split(c.str()); }, + [](const std::vector& races, const unit_filter_args& args) + { + return std::find(races.begin(), races.end(), args.u.race()->id()) != races.end(); } - } - if(!match_found) { - return false; - } - } + ); - if(!vcfg["defense"].blank()) { - bool match_found = false; - int actual_defense = u.defense_modifier(fc_.get_disp_context().map().get_terrain(loc)); - for(auto def : utils::parse_ranges(vcfg["defense"])) { - if(def.first <= actual_defense && actual_defense <= def.second) { - match_found = true; - break; + create_attribute(literal["gender"], + [](const config::attribute_value& c) { return string_gender(c.str()); }, + [](unit_race::GENDER gender, const unit_filter_args& args) + { + return gender == args.u.gender(); } - } - if(!match_found) { - return false; - } - } + ); - if(!vcfg["movement_cost"].blank()) { - bool match_found = false; - int actual_cost = u.movement_cost(fc_.get_disp_context().map().get_terrain(loc)); - for(auto cost : utils::parse_ranges(vcfg["movement_cost"])) { - if(cost.first <= actual_cost && actual_cost <= cost.second) { - match_found = true; - break; + create_attribute(literal["side"], + [](const config::attribute_value& c) + { + std::vector res; + for(const std::string& s : utils::split(c.str())) { + res.push_back(std::stoi(s)); + } + return res; + }, + [](const std::vector& sides, const unit_filter_args& args) + { + return std::find(sides.begin(), sides.end(), args.u.side()) != sides.end(); } - } - if(!match_found) { - return false; - } - } + ); - if(!vcfg["vision_cost"].blank()) { - bool match_found = false; - int actual_cost = u.vision_cost(fc_.get_disp_context().map().get_terrain(loc)); - for(auto cost : utils::parse_ranges(vcfg["vision_cost"])) { - if(cost.first <= actual_cost && actual_cost <= cost.second) { - match_found = true; - break; + create_attribute(literal["status"], + [](const config::attribute_value& c) { return utils::split(c.str()); }, + [](const std::vector& statuses, const unit_filter_args& args) + { + for(const std::string& status : statuses) { + if (args.u.get_state(status)) { + return true; + } + } + return false; } - } - if(!match_found) { - return false; - } - } + ); - if(!vcfg["jamming_cost"].blank()) { - bool match_found = false; - int actual_cost = u.jamming_cost(fc_.get_disp_context().map().get_terrain(loc)); - for(auto cost : utils::parse_ranges(vcfg["jamming_cost"])) { - if(cost.first <= actual_cost && actual_cost <= cost.second) { - match_found = true; - break; - } - } - if(!match_found) { - return false; - } - } + create_attribute(literal["has_weapon"], + [](const config::attribute_value& c) { return c.str(); }, + [](const std::string& weapon, const unit_filter_args& args) + { - // Now start with the new WML based comparison. - // If a key is in the unit and in the filter, they should match - // filter only => not for us - // unit only => not filtered - config unit_cfg; // No point in serializing the unit once for each [filter_wml]! - for (const vconfig& wmlcfg : vcfg.get_children("filter_wml")) { - config fwml = wmlcfg.get_parsed_config(); - /* Check if the filter only cares about variables. - If so, no need to serialize the whole unit. */ - config::all_children_itors ci = fwml.all_children_range(); - if (fwml.all_children_count() == 1 && - fwml.attribute_count() == 1 && - ci.front().key == "variables") { - if (!u.variables().matches(ci.front().cfg)) - return false; - } else { - if (unit_cfg.empty()) - u.write(unit_cfg); - if (!unit_cfg.matches(fwml)) - return false; + for(const attack_type& a : args.u.attacks()) { + if(a.id() == weapon) { + return true; + } + } + return false; } - } - - for (const vconfig& vision : vcfg.get_children("filter_vision")) { - std::set viewers; + ); - // Use standard side filter - side_filter ssf(vision, &fc_); - std::vector sides = ssf.get_teams(); - viewers.insert(sides.begin(), sides.end()); - - bool found = false; - for (const int viewer : viewers) { - bool fogged = fc_.get_disp_context().get_team(viewer).fogged(loc); - bool hiding = u.invisible(loc, fc_.get_disp_context()) - && fc_.get_disp_context().get_team(viewer).is_enemy(u.side()); - bool unit_hidden = fogged || hiding; - if (vision["visible"].to_bool(true) != unit_hidden) { - found = true; - break; + create_attribute(literal["role"], + [](const config::attribute_value& c) { return c.str(); }, + [](const std::string& role, const unit_filter_args& args) + { + return args.u.get_role() == role; } - } - if (!found) {return false;} - } + ); - if (vcfg.has_child("filter_adjacent")) { - const unit_map& units = fc_.get_disp_context().units(); - map_location adjacent[6]; - get_adjacent_tiles(loc, adjacent); + create_attribute(literal["ai_special"], + [](const config::attribute_value& c) { return c.str(); }, + [](const std::string& ai_special, const unit_filter_args& args) + { + return (ai_special == "guardian") == args.u.get_state(unit::STATE_GUARDIAN); + } + ); - for (const vconfig& adj_cfg : vcfg.get_children("filter_adjacent")) { - int match_count=0; - unit_filter filt(adj_cfg, &fc_, use_flat_tod_); + create_attribute(literal["canrecruit"], + [](const config::attribute_value& c) { return c.to_bool(); }, + [](bool canrecruit, const unit_filter_args& args) + { + return args.u.can_recruit() == canrecruit; + } + ); - config::attribute_value i_adjacent = adj_cfg["adjacent"]; - std::vector dirs; - if (i_adjacent.blank()) { - dirs = map_location::default_dirs(); - } else { - dirs = map_location::parse_directions(i_adjacent); + create_attribute(literal["recall_cost"], + [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, + [](const std::vector>& ranges, const unit_filter_args& args) + { + for(auto cost : ranges) { + if(cost.first <= args.u.recall_cost() && args.u.recall_cost() <= cost.second) { + return true; + } + } + return false; } + ); - std::vector::const_iterator j, j_end = dirs.end(); - for (j = dirs.begin(); j != j_end; ++j) { - unit_map::const_iterator unit_itor = units.find(adjacent[*j]); - if (unit_itor == units.end() || !filt(*unit_itor, u)) { - continue; + create_attribute(literal["level"], + [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, + [](const std::vector>& ranges, const unit_filter_args& args) + { + for(auto lvl : ranges) { + if(lvl.first <= args.u.level() && args.u.level() <= lvl.second) { + return true; + } } - boost::optional is_enemy; - if (!adj_cfg["is_enemy"].blank()) { - is_enemy = adj_cfg["is_enemy"].to_bool(); + return false; + } + ); + + create_attribute(literal["defense"], + [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, + [](const std::vector>& ranges, const unit_filter_args& args) + { + int actual_defense = args.u.defense_modifier(args.fc->get_disp_context().map().get_terrain(args.loc)); + for(auto def : ranges) { + if(def.first <= actual_defense && actual_defense <= def.second) { + return true; + } } - if (!is_enemy || *is_enemy == - fc_.get_disp_context().get_team(u.side()).is_enemy(unit_itor->side())) { - ++match_count; + return false; + } + ); + + create_attribute(literal["movement_cost"], + [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, + [](const std::vector>& ranges, const unit_filter_args& args) + { + int actual_cost = args.u.movement_cost(args.fc->get_disp_context().map().get_terrain(args.loc)); + for(auto cost : ranges) { + if(cost.first <= actual_cost && actual_cost <= cost.second) { + return true; + } } + return false; } + ); - static std::vector > default_counts = utils::parse_ranges("1-6"); - config::attribute_value i_count = adj_cfg["count"]; - if(!in_ranges(match_count, !i_count.blank() ? utils::parse_ranges(i_count) : default_counts)) { + create_attribute(literal["vision_cost"], + [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, + [](const std::vector>& ranges, const unit_filter_args& args) + { + int actual_cost = args.u.vision_cost(args.fc->get_disp_context().map().get_terrain(args.loc)); + for(auto cost : ranges) { + if(cost.first <= actual_cost && actual_cost <= cost.second) { + return true; + } + } return false; } - } - } + ); - if (!vcfg["find_in"].blank()) { - // Allow filtering by searching a stored variable of units - if (const game_data * gd = fc_.get_game_data()) { - try + create_attribute(literal["jamming_cost"], + [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, + [](const std::vector>& ranges, const unit_filter_args& args) { - variable_access_const vi = gd->get_variable_access_read(vcfg["find_in"]); - bool found_id = false; - for (const config& c : vi.as_array()) - { - if(c["id"] == u.id()) - found_id = true; + int actual_cost = args.u.jamming_cost(args.fc->get_disp_context().map().get_terrain(args.loc)); + for(auto cost : ranges) { + if(cost.first <= actual_cost && actual_cost <= cost.second) { + return true; + } } - if(!found_id) - { + return false; + } + ); + + create_attribute(literal["lua_function"], + [](const config::attribute_value& c) { return c.str(); }, + [](const std::string& lua_function, const unit_filter_args& args) + { + if (game_lua_kernel * lk = args.fc->get_lua_kernel()) { + return lk->run_filter(lua_function.c_str(), args.u); + } + return true; + } + ); + + create_attribute(literal["formula"], + [](const config::attribute_value& c) + { + //TODO: catch syntax error. + return wfl::formula(c, new wfl::gamestate_function_symbol_table()); + }, + [](const wfl::formula& form, const unit_filter_args& args) + { + try { + const wfl::unit_callable main(args.loc, args.u); + wfl::map_formula_callable callable(main.fake_ptr()); + if (args.u2) { + std::shared_ptr secondary(new wfl::unit_callable(*args.u2)); + callable.add("other", wfl::variant(secondary)); + // It's not destroyed upon scope exit because the variant holds a reference + } + if(!form.evaluate(callable).as_bool()) { + return false; + } + return true; + } catch(wfl::formula_error& e) { + lg::wml_error() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n"; + // Formulae with syntax errors match nothing return false; } } - catch(const invalid_variablename_exception&) + ); + + create_attribute(literal["find_in"], + [](const config::attribute_value& c) { return c.str(); }, + [](const std::string& find_in, const unit_filter_args& args) { - return false; + // Allow filtering by searching a stored variable of units + if (const game_data * gd = args.fc->get_game_data()) { + try + { + for (const config& c : gd->get_variable_access_read(find_in).as_array()) + { + if(c["id"] == args.u.id()) { + return true; + } + } + return false; + } + catch(const invalid_variablename_exception&) + { + return false; + } + } + return true; } + ); + + if (!literal["x"].empty() || !literal["y"].empty()) { + children_.emplace_back(new unit_filter_xy(literal["x"], literal["y"])); } - } - if (!vcfg["formula"].blank()) { - try { - const wfl::unit_callable main(loc,u); - wfl::map_formula_callable callable(main.fake_ptr()); - if (u2) { - std::shared_ptr secondary(new wfl::unit_callable(*u2)); - callable.add("other", wfl::variant(secondary)); - // It's not destroyed upon scope exit because the variant holds a reference - } - const wfl::formula form(vcfg["formula"], new wfl::gamestate_function_symbol_table); - if(!form.evaluate(callable).as_bool()) { - return false; + + for(auto child : cfg.all_ordered()) { + CONDITIONAL_TYPE cond; + if(cond.parse(child.first)) { + cond_children_.emplace_back(std::piecewise_construct_t(), std::make_tuple(cond), std::make_tuple(child.second)); } - return true; - } catch(wfl::formula_error& e) { - lg::wml_error() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n"; - // Formulae with syntax errors match nothing - return false; - } - } - - if (!vcfg["lua_function"].blank()) { - if (game_lua_kernel * lk = fc_.get_lua_kernel()) { - bool b = lk->run_filter(vcfg["lua_function"].str().c_str(), u); - if (!b) return false; + else if (child.first == "filter_wml") { + create_child(child.second, [](const vconfig& c, const unit_filter_args& args) { + config fwml = c.get_parsed_config(); + + /* Check if the filter only cares about variables. + If so, no need to serialize the whole unit. */ + config::all_children_itors ci = fwml.all_children_range(); + if (fwml.all_children_count() == 1 && fwml.attribute_count() == 1 && ci.front().key == "variables") { + return args.u.variables().matches(ci.front().cfg); + } else { + config ucfg; + args.u.write(ucfg); + return ucfg.matches(fwml); + } + }); + } + else if (child.first == "filter_vision") { + create_child(child.second, [](const vconfig& c, const unit_filter_args& args) { + std::set viewers; + side_filter ssf(c, args.fc); + std::vector sides = ssf.get_teams(); + viewers.insert(sides.begin(), sides.end()); + + for (const int viewer : viewers) { + bool fogged = args.fc->get_disp_context().get_team(viewer).fogged(args.loc); + bool hiding = args.u.invisible(args.loc, args.fc->get_disp_context()) && args.fc->get_disp_context().get_team(viewer).is_enemy(args.u.side()); + bool unit_hidden = fogged || hiding; + if (c["visible"].to_bool(true) != unit_hidden) { + return true; + } + } + return false; + }); + } + else if (child.first == "filter_adjacent") { + children_.emplace_back(new unit_filter_adjacent(child.second)); + } + else if (child.first == "filter_location") { + create_child(child.second, [](const vconfig& c, const unit_filter_args& args) { + return terrain_filter(c, args.fc, args.use_flat_tod).match(args.loc); + }); + } + else if (child.first == "filter_side") { + create_child(child.second, [](const vconfig& c, const unit_filter_args& args) { + return side_filter(c, args.fc).match(args.u.side()); + }); + } + else if (child.first == "has_attack") { + create_child(child.second, [](const vconfig& c, const unit_filter_args& args) { + for(const attack_type& a : args.u.attacks()) { + if(a.matches_filter(c.get_parsed_config())) { + return true; + } + } + return false; + }); + } + else { + std::stringstream errmsg; + errmsg << "encountered a child [" << child.first << "] of a standard unit filter, it is being ignored"; + DBG_CF << errmsg.str() << std::endl; + } + } } - return true; -} -std::vector basic_unit_filter_impl::all_matches_on_map(unsigned max_matches, const map_location* loc, const unit* u2) const { - std::vector ret; - for (const unit & u : fc_.get_disp_context().units()) { - if (matches(u, loc ? *loc : u.get_location(), u2)) { - if(max_matches == 0) { - return ret; - } - --max_matches; - ret.push_back(&u); - } - } - return ret; -} -unit_const_ptr basic_unit_filter_impl::first_match_on_map() const { - const unit_map & units = fc_.get_disp_context().units(); - for(unit_map::const_iterator u = units.begin(); u != units.end(); u++) { - if (matches(*u,u->get_location(),nullptr)) { - return u.get_shared_ptr(); - } - } - return unit_const_ptr(); -} diff --git a/src/units/filter.hpp b/src/units/filter.hpp index 63644090e222..b73e2c439184 100644 --- a/src/units/filter.hpp +++ b/src/units/filter.hpp @@ -26,6 +26,13 @@ #pragma once #include "units/ptr.hpp" +#include "utils/make_enum.hpp" + +#include "display_context.hpp" +#include "units/map.hpp" +#include "units/unit.hpp" +#include "filter_context.hpp" +#include "variable.hpp" #include #include @@ -36,28 +43,71 @@ class config; class vconfig; struct map_location; -class unit_filter_abstract_impl { -public: - virtual bool matches(const unit & u, const map_location & loc, const unit * u2 = nullptr) const = 0; - virtual std::vector all_matches_on_map(unsigned max_matches, const map_location* loc = nullptr, const unit* u2 = nullptr) const = 0; - virtual unit_const_ptr first_match_on_map() const = 0; - virtual config to_config() const = 0; - virtual bool empty() const {return false;} - virtual ~unit_filter_abstract_impl() {} -}; -class unit_filter { -public: - unit_filter(const vconfig & cfg, const filter_context * fc, bool use_flat_tod = false); //!< Constructs a unit filter from a config and a context. This function should give the most efficient implementation available. - // Copy and Swap Idiom for the interface -- does not copy the underlying impl - unit_filter(const unit_filter & o ) : impl_(o.impl_), max_matches_() {} - void swap(unit_filter & o) { - impl_.swap(o.impl_); - std::swap(max_matches_, o.max_matches_); +namespace unit_filter_impl +{ + MAKE_ENUM (CONDITIONAL_TYPE, + (AND, "and") + (OR, "or") + (NOT, "not") + ) + + struct unit_filter_args + { + const unit& u; + const map_location loc; + const unit* u2; + const filter_context * fc; + const bool use_flat_tod; + }; + + struct unit_filter_base + { + virtual bool matches(const unit_filter_args&) const = 0; + virtual ~unit_filter_base() {} + }; + + struct unit_filter_compound : public unit_filter_base + { + unit_filter_compound(vconfig cfg); + + template + void create_attribute(const config::attribute_value c, C conv, F func); + template + void create_child(const vconfig& c, F func); + + void fill(vconfig cfg); + + virtual bool matches(const unit_filter_args& u) const override; + bool filter_impl(const unit_filter_args& u) const; + + std::vector> children_; + std::vector> cond_children_; + }; + +} + +class unit_filter +{ +public: + unit_filter(vconfig cfg, const filter_context * fc) + : cfg_(cfg) + , fc_(fc) + , use_flat_tod_(false) + , impl_(cfg_) + , max_matches_(-1) + { } - unit_filter & operator=(unit_filter o) { - swap(o); + + unit_filter(const unit_filter&) = default; + unit_filter(unit_filter&&) = default; + + unit_filter& operator=(const unit_filter&) = default; + unit_filter& operator=(unit_filter&&) = default; + + unit_filter& set_use_flat_tod(bool value) { + use_flat_tod_ = value; return *this; } @@ -65,18 +115,26 @@ class unit_filter { /// Use this for units on a recall list, or to test for a match if /// a unit is hypothetically moved. bool matches(const unit & u, const map_location & loc) const { - return impl_->matches(u,loc); + return impl_.matches(unit_filter_impl::unit_filter_args{u, loc, nullptr, fc_, use_flat_tod_}); } + /// Determine if *this matches @a filter at its current location. /// (Only use for units currently on the map; otherwise use the overload /// that takes a location, possibly with a null location.) - bool matches(const unit & u) const; + bool matches(const unit & u) const { + return impl_.matches(unit_filter_impl::unit_filter_args{u, u.get_location(), nullptr, fc_, use_flat_tod_}); + } + + bool matches(const unit & u, const map_location & loc, const unit & u2) const { + return impl_.matches(unit_filter_impl::unit_filter_args{u, loc, &u2, fc_, use_flat_tod_}); + } - bool matches(const unit & u, const map_location & loc, const unit & u2) const; - bool matches(const unit & u, const unit & u2) const; + bool matches(const unit & u, const unit & u2) const { + return impl_.matches(unit_filter_impl::unit_filter_args{u, u.get_location(), &u2, fc_, use_flat_tod_}); + } bool operator()(const unit & u, const map_location & loc) const { - return matches(u,loc); + return matches(u, loc); } bool operator()(const unit & u) const { @@ -84,39 +142,66 @@ class unit_filter { } bool operator()(const unit & u, const map_location & loc, const unit & u2) const { - return matches(u,loc,u2); + return matches(u, loc, u2); } bool operator()(const unit & u, const unit & u2) const { - return matches(u,u2); + return matches(u, u2); } - std::vector all_matches_on_map() const { - return impl_->all_matches_on_map(max_matches_); + std::vector all_matches_on_map(const map_location* loc = nullptr, const unit* other_unit = nullptr) const + { + std::vector ret; + int max_matches = max_matches_; + + for (const unit & u : fc_->get_disp_context().units()) { + if (impl_.matches(unit_filter_impl::unit_filter_args{u, loc ? *loc : u.get_location(), other_unit, fc_, use_flat_tod_})) { + if(max_matches == 0) { + return ret; + } + --max_matches; + ret.push_back(&u); + } + } + return ret; } std::vector all_matches_at(const map_location& loc) const { - return impl_->all_matches_on_map(max_matches_, &loc); + return all_matches_on_map(&loc); } std::vector all_matches_with_unit(const unit& u) const { - return impl_->all_matches_on_map(max_matches_, nullptr, &u); + return all_matches_on_map(nullptr, &u); } std::vector all_matches_with_unit_at(const unit& u, const map_location& loc) const { - return impl_->all_matches_on_map(max_matches_, &loc, &u); + return all_matches_on_map(&loc, &u); } unit_const_ptr first_match_on_map() const { - return impl_->first_match_on_map(); + const unit_map & units = fc_->get_disp_context().units(); + for(unit_map::const_iterator u = units.begin(); u != units.end(); u++) { + if (matches(*u, u->get_location())) { + return u.get_shared_ptr(); + } + } + return unit_const_ptr(); } - config to_config() const; + config to_config() const { + return cfg_.get_config(); + } bool empty() const { - return impl_->empty(); + return cfg_.get_config().empty(); } + private: - std::shared_ptr impl_; - unsigned max_matches_; + + vconfig cfg_; + const filter_context * fc_; + bool use_flat_tod_; + unit_filter_impl::unit_filter_compound impl_; + int max_matches_; }; + diff --git a/src/variable.cpp b/src/variable.cpp index e17b7803d39d..7333b0d5a8de 100644 --- a/src/variable.cpp +++ b/src/variable.cpp @@ -124,16 +124,17 @@ vconfig vconfig::unconstructed_vconfig() * It is perfectly safe to call this for a vconfig that already manages its memory. * This does not work on a null() vconfig. */ -void vconfig::make_safe() const +const vconfig& vconfig::make_safe() const { // Nothing to do if we already manage our own memory. if ( memory_managed() ) - return; + return *this; // Make a copy of our config. cache_.reset(new config(*cfg_)); // Use our copy instead of the original. cfg_ = cache_.get(); + return *this; } config vconfig::get_parsed_config() const diff --git a/src/variable.hpp b/src/variable.hpp index d25700bfa776..a52100e27f4e 100644 --- a/src/variable.hpp +++ b/src/variable.hpp @@ -60,6 +60,7 @@ class vconfig /// Equivalent to vconfig(cfg, false). /// Do not use if the vconfig will persist after @a cfg is destroyed! explicit vconfig(const config &cfg) : cache_(), cfg_(&cfg) {} + explicit vconfig(config &&cfg) : cache_(new config(std::move(cfg))), cfg_(cache_.get()) { } vconfig(const config &cfg, bool manage_memory); ~vconfig(); @@ -70,7 +71,7 @@ class vconfig explicit operator bool() const { return !null(); } bool null() const { assert(cfg_); return cfg_ == &default_empty_config; } - void make_safe() const; //!< instruct the vconfig to make a private copy of its underlying data. + const vconfig& make_safe() const; //!< instruct the vconfig to make a private copy of its underlying data. const config& get_config() const { return *cfg_; } config get_parsed_config() const;