Prevented t_string non-const references from being returned by functions.

(Original patch #1139 by Frank Richter.)

This avoids leaking an implementation detail from the config class and
opens the door to a considerable reduction of the memory footprint of
lexical data.
This commit is contained in:
Guillaume Melquiond 2010-03-27 09:32:58 +00:00
parent ddd0dab6f0
commit 1896b603f1
16 changed files with 71 additions and 47 deletions

View file

@ -82,6 +82,9 @@
comment = "Naming hotseat players, delay shroud updates on game start."
ircuser = "gabm"
[/entry]
[entry]
name = "Frank Richter (res)"
[/entry]
[entry]
name = "Greg Copeland (Oracle)"
comment = "coding; Server optimizations."

View file

@ -396,13 +396,6 @@ void config::remove_child(const std::string& key, size_t index)
delete res;
}
t_string& config::operator[](const std::string& key)
{
check_valid();
return values[key];
}
const t_string& config::operator[](const std::string& key) const
{
return get_attribute(key);

View file

@ -176,6 +176,39 @@ public:
Itor i_;
};
class proxy_string
{
t_string &real_str_;
public:
proxy_string(config &owner, const std::string &key)
: real_str_(owner.values[key]) {}
proxy_string &operator=(const proxy_string &other)
{ return this->operator=(other.real_str_); }
proxy_string& operator=(const char *str)
{ real_str_ = str; return *this; }
proxy_string& operator=(const t_string &str)
{ real_str_ = str; return *this; }
proxy_string& operator=(const std::string &str)
{ real_str_ = str; return *this; }
proxy_string& operator+=(const std::string &str)
{ real_str_ += str; return *this; }
// t_string 'emulation' methods
bool empty() const { return real_str_.empty(); }
const char *c_str() const { return real_str_.c_str(); }
std::string to_serialized() const { return real_str_.to_serialized(); }
const std::string &str() const { return real_str_.str(); }
operator std::string() const { return real_str_.str(); }
operator t_string() const { return real_str_; }
inline friend std::ostream& operator<<(std::ostream &os, const proxy_string &str)
{ return os << str.real_str_; }
};
typedef std::pair<const_attribute_iterator,const_attribute_iterator> const_attr_itors;
typedef std::pair<child_list::iterator, child_list::iterator> child_itors_bak;
@ -206,7 +239,9 @@ public:
config& add_child(const std::string& key);
config& add_child(const std::string& key, const config& val);
config& add_child_at(const std::string& key, const config& val, size_t index);
t_string& operator[](const std::string& key);
proxy_string operator[](const std::string& key)
{ return proxy_string(*this, key); }
const t_string& operator[](const std::string& key) const;
/**

View file

@ -30,13 +30,11 @@ class string_map_variable_set : public variable_set
public:
string_map_variable_set(const string_map& map) : map_(map) {};
virtual const t_string& get_variable_const (const std::string& key) const
virtual t_string get_variable_const (const std::string& key) const
{
static const t_string empty_string = "";
const string_map::const_iterator itor = map_.find(key);
if(itor == map_.end()) {
return empty_string;
if (itor == map_.end()) {
return t_string();
} else {
return itor->second;
}

View file

@ -176,7 +176,7 @@ namespace {
}
private:
static unsigned instance_count;
t_string x1_, x2_, y1_, y2_;
std::string x1_, x2_, y1_, y2_;
};
unsigned pump_manager::instance_count=0;
@ -1107,21 +1107,16 @@ WML_HANDLER_FUNCTION(move_unit_fake, /*event_info*/, cfg)
}
// Helper function(s) for [set_variable]
namespace {
bool isint(const std::string &var) {
return var.find('.') == std::string::npos;
}
bool isint(const t_string &var) {
return isint(var.str());
}
} // End anonymous namespace
static bool isint(const std::string &var) {
return var.find('.') == std::string::npos;
}
WML_HANDLER_FUNCTION(set_variable, /*event_info*/, cfg)
{
game_state *state_of_game = resources::state_of_game;
const std::string name = cfg["name"];
t_string& var = state_of_game->get_variable(name);
config::proxy_string var = state_of_game->get_variable(name);
const t_string &literal = cfg.get_config()["literal"]; // no $var substitution
if(literal.empty() == false) {
@ -1163,7 +1158,7 @@ WML_HANDLER_FUNCTION(set_variable, /*event_info*/, cfg)
const std::string multiply = cfg["multiply"];
if(multiply.empty() == false) {
if(isint(var) && isint(multiply)) {
if(isint(var.str()) && isint(multiply)) {
var = str_cast( std::atoi(var.c_str()) * std::atoi(multiply.c_str()) );
} else {
var = str_cast( std::atof(var.c_str()) * std::atof(multiply.c_str()) );
@ -1176,7 +1171,7 @@ WML_HANDLER_FUNCTION(set_variable, /*event_info*/, cfg)
ERR_NG << "division by zero on variable " << name << "\n";
return;
}
if(isint(var) && isint(divide)) {
if(isint(var.str()) && isint(divide)) {
var = str_cast( std::atoi(var.c_str()) / std::atoi(divide.c_str()) );
} else {
var = str_cast( std::atof(var.c_str()) / std::atof(divide.c_str()) );
@ -1189,7 +1184,7 @@ WML_HANDLER_FUNCTION(set_variable, /*event_info*/, cfg)
ERR_NG << "division by zero on variable " << name << "\n";
return;
}
if(isint(var) && isint(modulo)) {
if(isint(var.str()) && isint(modulo)) {
var = str_cast( std::atoi(var.c_str()) % std::atoi(modulo.c_str()) );
} else {
double value = std::fmod( std::atof(var.c_str()), std::atof(modulo.c_str()) );

View file

@ -462,16 +462,16 @@ void extract_summary_from_config(config& cfg_save, config& cfg_summary)
}
}
t_string& game_state::get_variable(const std::string& key)
config::proxy_string game_state::get_variable(const std::string& key)
{
return variable_info(key, true, variable_info::TYPE_SCALAR).as_scalar();
}
const t_string& game_state::get_variable_const(const std::string& key) const
t_string game_state::get_variable_const(const std::string& key) const
{
variable_info to_get(key, false, variable_info::TYPE_SCALAR);
if(!to_get.is_valid) {
t_string& to_return = temporaries[key];
config::proxy_string to_return = temporaries[key];
if (key.size() > 7 && key.substr(key.size()-7) == ".length") {
// length is a special attribute, so guarantee its correctness
to_return = "0";

View file

@ -96,8 +96,8 @@ public:
// Variable access
t_string& get_variable(const std::string& varname);
virtual const t_string& get_variable_const(const std::string& varname) const;
config::proxy_string get_variable(const std::string &varname);
virtual t_string get_variable_const(const std::string& varname) const;
config& get_variable_cfg(const std::string& varname);
variable_info::array_range get_variable_cfgs(const std::string& varname);

View file

@ -1490,8 +1490,8 @@ void connect::lists_init()
int side_num = 1;
foreach (config &side, sides)
{
t_string &team_name = side["team_name"];
t_string &user_team_name = side["user_team_name"];
config::proxy_string team_name = side["team_name"];
config::proxy_string user_team_name = side["user_team_name"];
if(team_name.empty())
team_name = lexical_cast<std::string>(side_num);
@ -1516,7 +1516,7 @@ void connect::lists_init()
foreach (config &side, sides)
{
const std::string side_num = lexical_cast<std::string>(_side_num);
t_string &team_name = side["team_name"];
config::proxy_string team_name = side["team_name"];
if(team_name.empty())
team_name = side_num;

View file

@ -320,8 +320,8 @@ LEVEL_RESULT playsingle_controller::play_scenario(
// Log before prestart events: they do weird things.
if (first_human_team_ != -1) { //sp logs
log.start(gamestate_, teams_[first_human_team_],
loading_game_ ? gamestate_.get_variable("turn_number") : "",
number_of_turns(), resources::game_map->write());
loading_game_ ? gamestate_.get_variable("turn_number").c_str() : "",
number_of_turns(), resources::game_map->write());
} else { //ai vs. ai upload logs
log.start(gamestate_, resources::game_map->write());
}

View file

@ -248,7 +248,7 @@ void _set_idle_anim_rate(const int rate)
preferences::set("idle_anim_rate", lexical_cast<std::string>(rate));
}
const std::string& language()
std::string language()
{
return prefs["locale"];
}

View file

@ -74,7 +74,7 @@ namespace preferences {
int idle_anim_rate();
void _set_idle_anim_rate(const int rate);
const std::string& language();
std::string language();
void set_language(const std::string& s);
// Don't rename it to sound() because of a gcc-3.3 branch bug,

View file

@ -33,7 +33,7 @@ class variable_set
public:
virtual ~variable_set();
virtual const t_string& get_variable_const(const std::string& id) const = 0;
virtual t_string get_variable_const(const std::string& id) const = 0;
};
/** The type we use to represent Unicode strings. */

View file

@ -166,13 +166,14 @@ static void expand_partialresolution(config& dst_cfg, const config& top_cfg)
// follow the inheritance hierarchy and push all the nodes on the stack
std::vector<const config*> parent_stack(1, (*i));
const config* parent;
const t_string* parent_id = &((**i)["inherits"]);
while (!*(parent = &top_cfg.find_child("resolution", "id", (*parent_id)))) {
parent = &top_cfg.find_child("partialresolution", "id", (*parent_id));
std::string parent_id = (**i)["inherits"];
while (!*(parent = &top_cfg.find_child("resolution", "id", parent_id)))
{
parent = &top_cfg.find_child("partialresolution", "id", parent_id);
if (!*parent)
throw config::error("[partialresolution] refers to non-existant [resolution] " + (*parent_id).str());
throw config::error("[partialresolution] refers to non-existant [resolution] " + parent_id);
parent_stack.push_back(parent);
parent_id = &((*parent)["inherits"]);
parent_id = (*parent)["inherits"];
}
// Add the parent resolution and apply all the modifications of its children

View file

@ -121,7 +121,6 @@ public:
operator t_string_base() const { return get(); }
t_string& operator=(const t_string& o) { super::operator=(o); return *this; }
t_string& operator=(const std::string& o) { super::operator=(base(o)); return *this; }
t_string& operator=(const char* o) { super::operator=(base(o)); return *this; }
t_string operator+(const t_string& o) const { return get() + o.get(); }

View file

@ -716,7 +716,7 @@ variable_info::variable_info(const std::string& varname,
}
}
t_string& variable_info::as_scalar() {
config::proxy_string variable_info::as_scalar() {
assert(is_valid);
return (*vars)[key];
}

View file

@ -211,7 +211,7 @@ public:
* Results: after deciding the desired type, these methods can retrieve the result
* Note: first you should force_valid or check is_valid, otherwise these may fail
*/
t_string& as_scalar();
config::proxy_string as_scalar();
config& as_container();
array_range as_array(); //range may be empty
};