Unit: converted upkeep handling to std::variant (#5459)
Since MacOS doesn't properly support std::variant (it has problems with std::visit specifically) unless it targets MacOS 10.14 or later, this adds a wrapper class that uses std::variant and friends on all platforms except MacOS, where boost::variant is used. The USING_BOOST_VARIANT define is then defined on MacOS and should be used to guard for code (such as inheriting from boost::static_visitor) that isn't needed with std::variant. unit::upkeep_parser_visitor is still unconditionally boost::variant-compatible since it's used by const_attribute_value::apply_visitor, and configs still use boost::variant under the hood. Additionally, these visitors are used in multiple places, so I can't convert them to lambas in-class. Finally, I used brace-init for the upkeep types and visitors to make it clearer these are not functions. As for the static_cast changes, for some reason mingw had issues converting double or long long int to the upkeep variant (which keeps an int specifically). No idea why it had no problem dealing with this with boost::variant.
This commit is contained in:
parent
b10e061b24
commit
ee28ac0e26
7 changed files with 108 additions and 26 deletions
|
@ -372,9 +372,11 @@ void advance_unit(map_location loc, const advancement_option &advance_to, bool f
|
|||
std::vector<int> not_seeing = actions::get_sides_not_seeing(*u);
|
||||
|
||||
// Create the advanced unit.
|
||||
bool use_amla = boost::get<std::string>(&advance_to) == nullptr;
|
||||
unit_ptr new_unit = use_amla ? get_amla_unit(*u, *boost::get<const config*>(advance_to)) :
|
||||
get_advanced_unit(*u, boost::get<std::string>(advance_to));
|
||||
const bool use_amla = !utils::holds_alternative<std::string>(advance_to);
|
||||
unit_ptr new_unit = use_amla
|
||||
? get_amla_unit(*u, *utils::get<const config*>(advance_to))
|
||||
: get_advanced_unit(*u, utils::get<std::string>(advance_to));
|
||||
|
||||
new_unit->set_location(loc);
|
||||
if ( !use_amla )
|
||||
{
|
||||
|
|
|
@ -23,11 +23,12 @@ class team;
|
|||
class unit;
|
||||
class config;
|
||||
|
||||
#include "map/location.hpp"
|
||||
#include "units/attack_type.hpp"
|
||||
#include "units/ptr.hpp"
|
||||
#include "units/race.hpp"
|
||||
#include "units/attack_type.hpp"
|
||||
#include "utils/variant.hpp"
|
||||
|
||||
#include <map/location.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -62,7 +63,7 @@ unit_ptr get_advanced_unit(const unit &u, const std::string &advance_to);
|
|||
*/
|
||||
unit_ptr get_amla_unit(const unit &u, const config &mod_option);
|
||||
|
||||
using advancement_option = boost::variant<std::string /*change type*/, const config* /*apply amla*/>;
|
||||
using advancement_option = utils::variant<std::string /*change type*/, const config* /*apply amla*/>;
|
||||
|
||||
/**
|
||||
* Function which will advance the unit at @a loc to 'advance_to'.
|
||||
|
|
|
@ -342,10 +342,10 @@ static int impl_unit_get(lua_State *L)
|
|||
unit::upkeep_t upkeep = u.upkeep_raw();
|
||||
|
||||
// Need to keep these separate in order to ensure an int value is always used if applicable.
|
||||
if(int* v = boost::get<int>(&upkeep)) {
|
||||
if(int* v = utils::get_if<int>(&upkeep)) {
|
||||
lua_push(L, *v);
|
||||
} else {
|
||||
const std::string type = boost::apply_visitor(unit::upkeep_type_visitor(), upkeep);
|
||||
const std::string type = utils::visit(unit::upkeep_type_visitor{}, upkeep);
|
||||
lua_push(L, type);
|
||||
}
|
||||
|
||||
|
@ -469,7 +469,7 @@ static int impl_unit_set(lua_State *L)
|
|||
|
||||
if(strcmp(m, "upkeep") == 0) {
|
||||
if(lua_isnumber(L, 3)) {
|
||||
u.set_upkeep(luaL_checkinteger(L, 3));
|
||||
u.set_upkeep(static_cast<int>(luaL_checkinteger(L, 3)));
|
||||
return 0;
|
||||
}
|
||||
const char* v = luaL_checkstring(L, 3);
|
||||
|
|
|
@ -442,7 +442,7 @@ void unit_filter_compound::fill(vconfig cfg)
|
|||
},
|
||||
[](unit::upkeep_t upkeep, const unit_filter_args& args)
|
||||
{
|
||||
return args.u.upkeep() == boost::apply_visitor(unit::upkeep_value_visitor(args.u), upkeep);
|
||||
return args.u.upkeep() == utils::visit(unit::upkeep_value_visitor{args.u}, upkeep);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -395,7 +395,7 @@ unit::unit(unit_ctor_t)
|
|||
, ellipse_()
|
||||
, random_traits_(true)
|
||||
, generate_name_(true)
|
||||
, upkeep_(upkeep_full())
|
||||
, upkeep_(upkeep_full{})
|
||||
, changed_attributes_(0)
|
||||
, invisibility_cache_()
|
||||
{
|
||||
|
@ -684,7 +684,7 @@ void unit::init(const unit_type& u_type, int side, bool real_unit, unit_race::GE
|
|||
side_ = side;
|
||||
gender_ = gender != unit_race::NUM_GENDERS ? gender : generate_gender(u_type, real_unit);
|
||||
facing_ = static_cast<map_location::DIRECTION>(randomness::rng::default_instance().get_random_int(0, map_location::NDIRECTIONS-1));
|
||||
upkeep_ = upkeep_full();
|
||||
upkeep_ = upkeep_full{};
|
||||
|
||||
// Apply the unit type's data to this unit.
|
||||
advance_to(u_type, real_unit);
|
||||
|
@ -951,7 +951,7 @@ void unit::advance_to(const unit_type& u_type, bool use_traits)
|
|||
|
||||
flag_rgb_ = new_type.flag_rgb();
|
||||
|
||||
upkeep_ = upkeep_full();
|
||||
upkeep_ = upkeep_full{};
|
||||
parse_upkeep(new_type.get_cfg()["upkeep"]);
|
||||
|
||||
anim_comp_->reset_after_advance(&new_type);
|
||||
|
@ -1585,12 +1585,12 @@ int unit::upkeep() const
|
|||
return 0;
|
||||
}
|
||||
|
||||
return boost::apply_visitor(upkeep_value_visitor(*this), upkeep_);
|
||||
return utils::visit(upkeep_value_visitor{*this}, upkeep_);
|
||||
}
|
||||
|
||||
bool unit::loyal() const
|
||||
{
|
||||
return boost::get<upkeep_loyal>(&upkeep_) != nullptr;
|
||||
return utils::holds_alternative<upkeep_loyal>(upkeep_);
|
||||
}
|
||||
|
||||
int unit::defense_modifier(const t_translation::terrain_code & terrain) const
|
||||
|
@ -2052,7 +2052,7 @@ void unit::apply_builtin_effect(std::string apply_to, const config& effect)
|
|||
set_max_experience(utils::apply_modifier(max_experience_, increase, 1));
|
||||
}
|
||||
} else if(apply_to == upkeep_loyal::type()) {
|
||||
upkeep_ = upkeep_loyal();
|
||||
upkeep_ = upkeep_loyal{};
|
||||
} else if(apply_to == "status") {
|
||||
const std::string& add = effect["add"];
|
||||
const std::string& remove = effect["remove"];
|
||||
|
@ -2626,16 +2626,16 @@ void unit::parse_upkeep(const config::attribute_value& upkeep)
|
|||
}
|
||||
|
||||
try {
|
||||
upkeep_ = upkeep.apply_visitor(upkeep_parser_visitor());
|
||||
upkeep_ = upkeep.apply_visitor(upkeep_parser_visitor{});
|
||||
} catch(std::invalid_argument& e) {
|
||||
WRN_UT << "Found invalid upkeep=\"" << e.what() << "\" in a unit" << std::endl;
|
||||
upkeep_ = upkeep_full();
|
||||
upkeep_ = upkeep_full{};
|
||||
}
|
||||
}
|
||||
|
||||
void unit::write_upkeep(config::attribute_value& upkeep) const
|
||||
{
|
||||
upkeep = boost::apply_visitor(upkeep_type_visitor(), upkeep_);
|
||||
upkeep = utils::visit(upkeep_type_visitor{}, upkeep_);
|
||||
}
|
||||
|
||||
void unit::clear_changed_attributes()
|
||||
|
|
|
@ -21,12 +21,14 @@
|
|||
#include "units/attack_type.hpp"
|
||||
#include "units/race.hpp"
|
||||
#include "units/alignment.hpp"
|
||||
#include <optional>
|
||||
#include "utils/variant.hpp"
|
||||
|
||||
#include <bitset>
|
||||
#include <boost/dynamic_bitset_fwd.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include <bitset>
|
||||
#include <optional>
|
||||
|
||||
class display;
|
||||
class team;
|
||||
class unit_animation_component;
|
||||
|
@ -1124,8 +1126,13 @@ public:
|
|||
static std::string type() { static std::string v = "loyal"; return v; }
|
||||
};
|
||||
|
||||
using upkeep_t = utils::variant<upkeep_full, upkeep_loyal, int>;
|
||||
|
||||
/** Visitor helper class to fetch the appropriate upkeep value. */
|
||||
class upkeep_value_visitor : public boost::static_visitor<int>
|
||||
class upkeep_value_visitor
|
||||
#ifdef USING_BOOST_VARIANT
|
||||
: public boost::static_visitor<int>
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
explicit upkeep_value_visitor(const unit& unit) : u_(unit) {}
|
||||
|
@ -1152,7 +1159,10 @@ public:
|
|||
};
|
||||
|
||||
/** Visitor helper struct to fetch the upkeep type flag if applicable, or the the value otherwise. */
|
||||
struct upkeep_type_visitor : public boost::static_visitor<std::string>
|
||||
struct upkeep_type_visitor
|
||||
#ifdef USING_BOOST_VARIANT
|
||||
: public boost::static_visitor<std::string>
|
||||
#endif
|
||||
{
|
||||
template<typename T>
|
||||
std::enable_if_t<!std::is_same_v<int, T>, std::string>
|
||||
|
@ -1168,8 +1178,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
using upkeep_t = boost::variant<upkeep_full, upkeep_loyal, int>;
|
||||
|
||||
/** Visitor helper class to parse the upkeep value from a config. */
|
||||
class upkeep_parser_visitor : public boost::static_visitor<upkeep_t>
|
||||
{
|
||||
|
@ -1180,7 +1188,7 @@ public:
|
|||
{
|
||||
if(n == 0) return upkeep_loyal();
|
||||
if(n < 0) throw std::invalid_argument(std::to_string(n));
|
||||
return n;
|
||||
return static_cast<int>(n);
|
||||
}
|
||||
|
||||
template<typename B>
|
||||
|
|
71
src/utils/variant.hpp
Normal file
71
src/utils/variant.hpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright (C) 2021 by the Battle for Wesnoth Project https://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* MacOS doesn't support std::visit when targing MacOS < 10.14 (currently we target 10.11).
|
||||
* This provides a wrapper around the STL variant API on all platforms except MacOS, which
|
||||
* instead utilizes boost::variant.
|
||||
*/
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define USING_BOOST_VARIANT
|
||||
#endif
|
||||
|
||||
#ifndef USING_BOOST_VARIANT
|
||||
#include <variant>
|
||||
#else
|
||||
#include <boost/variant.hpp>
|
||||
#endif
|
||||
|
||||
namespace utils
|
||||
{
|
||||
#ifndef USING_BOOST_VARIANT
|
||||
|
||||
using std::get;
|
||||
using std::get_if;
|
||||
using std::holds_alternative;
|
||||
using std::monostate;
|
||||
using std::variant;
|
||||
using std::visit;
|
||||
|
||||
#else
|
||||
|
||||
using boost::get;
|
||||
using boost::variant;
|
||||
|
||||
using monostate = boost::blank;
|
||||
|
||||
template<typename... Args>
|
||||
inline auto visit(Args&&... args) -> decltype(boost::apply_visitor(std::forward<Args>(args)...))
|
||||
{
|
||||
return boost::apply_visitor(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T, typename V>
|
||||
inline bool holds_alternative(const V& variant)
|
||||
{
|
||||
return boost::get<T>(&variant) != nullptr;
|
||||
}
|
||||
|
||||
template<typename T, typename V>
|
||||
inline T* get_if(V* variant)
|
||||
{
|
||||
return boost::get<T>(variant);
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace utils
|
Loading…
Add table
Reference in a new issue