SummerOfCodeProposal res
A bit about me
My name is Frank Richter, currently studying Computer Sciences at the University of Jena (that's in Germany).
As far as C++ is concerned, I program in it since at least seven years now. In my spare time I also develop for Crystal Space, an open source 3D engine. While I did a lot in the topics of realtime 3D, OpenGL, shaders and such, I also did (and do) 'low-level' things like cross-platform services and, last, but in this context here now certainly not least, matters of memory optimization.
Current status
- Upon reading the blog post linked in the idea proposal my first thought was to use a proxy class for operator[]. Below a patch that implements it.
Proxy class patch
This builds and, I believe, works, but has a number of not-so-nice issues:
- Some places store return values in t_string&. The idea is to change these to config::proxy_string, as that type should ideally behave like t_string&.
- config::proxy_string can cast to both std::string and t_string.
The first is necessary as t_string can cast to it as well; the second is necessary to support use of a config::proxy_string where a const t_string& is expected. Unfortunately, this introduces an ambiguity in cases where a config::proxy_string is passed to methods that can take either of std::string or t_string.
- the variables_set class has a member (get_variable_const()) that returns a t_string&. game_state descends from this class; it's get_variable_const() implementation supposedly returns a value from a config. To ensure the ref is valid, the value as a t_string is stored in a map. This is clearly not ideal.
Index: src/config.cpp =================================================================== --- src/config.cpp (revision 33970) +++ src/config.cpp (working copy) @@ -293,10 +293,12 @@ delete res; } +/* t_string& config::operator[](const std::string& key) { return values[key]; } +*/ const t_string& config::operator[](const std::string& key) const { Index: src/font.cpp =================================================================== --- src/font.cpp (revision 33970) +++ src/font.cpp (working copy) @@ -1278,7 +1278,7 @@ known_fonts.insert(font["name"]); } - font_order = (*fonts_config)["order"]; + font_order = (*fonts_config)["order"].t_str(); const std::vector<std::string> font_order = utils::split((*fonts_config)["order"]); std::vector<font::subset_descriptor> fontlist; std::vector<std::string>::const_iterator font; Index: src/playsingle_controller.cpp =================================================================== --- src/playsingle_controller.cpp (revision 33970) +++ src/playsingle_controller.cpp (working copy) @@ -281,7 +281,7 @@ // Log before prestart events: they do weird things. if (first_human_team_ != -1) { log.start(gamestate_, teams_[first_human_team_], first_human_team_ + 1, units_, - loading_game_ ? gamestate_.get_variable("turn_number") : "", status_.number_of_turns()); + loading_game_ ? gamestate_.get_variable("turn_number").c_str() : "", status_.number_of_turns()); } fire_prestart(!loading_game_); Index: src/gamestatus.cpp =================================================================== --- src/gamestatus.cpp (revision 33970) +++ src/gamestatus.cpp (working copy) @@ -1166,7 +1166,7 @@ } } -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(); } @@ -1174,8 +1174,9 @@ 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) return temporaries[key]; - return to_get.as_scalar(); + config::proxy_string val ((!to_get.is_valid) ? temporaries[key] : to_get.as_scalar()); + save_t_string[key] = val.t_str(); + return save_t_string[key]; } config& game_state::get_variable_cfg(const std::string& key) Index: src/theme.cpp =================================================================== --- src/theme.cpp (revision 33970) +++ src/theme.cpp (working copy) @@ -169,13 +169,13 @@ // 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))) == NULL) { - parent = top_cfg.find_child("partialresolution", "id", (*parent_id)); + t_string parent_id = ((**i)["inherits"]); + while((parent = top_cfg.find_child("resolution", "id", parent_id)) == NULL) { + parent = top_cfg.find_child("partialresolution", "id", parent_id); if(parent == NULL) - throw config::error("[partialresolution] refers to non-existant [resolution] " + (*parent_id).str()); + throw config::error("[partialresolution] refers to non-existant [resolution] " + parent_id.str()); 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 Index: src/variable.hpp =================================================================== --- src/variable.hpp (revision 33970) +++ src/variable.hpp (working copy) @@ -206,7 +206,7 @@ * 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 }; Index: src/game_config.cpp =================================================================== --- src/game_config.cpp (revision 33970) +++ src/game_config.cpp (working copy) @@ -239,7 +239,7 @@ std::string id = (**teamC)["id"]; std::vector<Uint32> temp = string2rgb((**teamC)["rgb"]); team_rgb_range.insert(std::make_pair(id,color_range(temp))); - team_rgb_name[id] = (**teamC)["name"]; + team_rgb_name[id] = (**teamC)["name"].t_str(); //generate palette of same name; std::vector<Uint32> tp = palette(team_rgb_range[id]); if(tp.size()){ Index: src/config.hpp =================================================================== --- src/config.hpp (revision 33970) +++ src/config.hpp (working copy) @@ -150,6 +150,43 @@ private: Itor i_; }; + + class proxy_string + { + config& owner; + const std::string& key; + + t_string& real_str() { return owner.values[key]; } + const t_string& real_str() const { return owner.values[key]; } + public: + proxy_string (config& owner, const std::string& key) : owner (owner), key (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(); } + const t_string& t_str() const { return real_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; @@ -167,7 +204,10 @@ 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); + + //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; const t_string& get_attribute(const std::string& key) const; Index: src/game_events.cpp =================================================================== --- src/game_events.cpp (revision 33970) +++ src/game_events.cpp (working copy) @@ -88,10 +88,10 @@ class pump_manager { public: pump_manager() : - x1_(state_of_game->get_variable("x1")), - x2_(state_of_game->get_variable("x2")), - y1_(state_of_game->get_variable("y1")), - y2_(state_of_game->get_variable("y2")) + x1_(state_of_game->get_variable("x1").str()), + x2_(state_of_game->get_variable("x2").str()), + y1_(state_of_game->get_variable("y1").str()), + y2_(state_of_game->get_variable("y2").str()) { ++instance_count; } @@ -856,7 +856,7 @@ static void store_gold_side(bool store_side, const vconfig& cfg) { - t_string *gold_store; + config::proxy_string *gold_store = 0; std::string side = cfg["side"]; std::string var_name = cfg["variable"]; if(var_name.empty()) { @@ -882,11 +882,12 @@ state_of_game->get_variable(var_name+".user_team_name") = (*teams)[team_index].user_team_name(); state_of_game->get_variable(var_name+".colour") = (*teams)[team_index].map_colour_to(); - gold_store = &state_of_game->get_variable(var_name+".gold"); + gold_store = new config::proxy_string (state_of_game->get_variable(var_name+".gold")); } else { - gold_store = &state_of_game->get_variable(var_name); + gold_store = new config::proxy_string (state_of_game->get_variable(var_name)); } *gold_store = lexical_cast_default<std::string>((*teams)[team_index].gold(),""); + delete gold_store; } } @@ -1158,7 +1159,7 @@ assert(state_of_game != NULL); 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_attribute("literal"); // no $var substitution if(literal.empty() == false) { @@ -1191,7 +1192,7 @@ 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()) ); @@ -1204,7 +1205,7 @@ 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()) ); @@ -1217,7 +1218,7 @@ 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()) ); Index: src/gui/dialogs/addon_list.cpp =================================================================== --- src/gui/dialogs/addon_list.cpp (revision 33970) +++ src/gui/dialogs/addon_list.cpp (working copy) @@ -74,10 +74,10 @@ item["label"] = tmp; data.insert(std::make_pair("author", item)); - item["label"] = (**itor)["downloads"]; + item["label"] = (**itor)["downloads"].t_str(); data.insert(std::make_pair("downloads", item)); - item["label"] = (**itor)["size"]; + item["label"] = (**itor)["size"].t_str(); data.insert(std::make_pair("size", item)); list->add_row(data); Index: src/unit_types.cpp =================================================================== --- src/unit_types.cpp (revision 33970) +++ src/unit_types.cpp (working copy) @@ -791,8 +791,8 @@ build_created(cfg, mv_types, races, traits); } - type_name_ = cfg_["name"]; - description_ = cfg_["description"]; + type_name_ = cfg_["name"].t_str(); + description_ = cfg_["description"].t_str(); hitpoints_ = lexical_cast_default<int>(cfg["hitpoints"], 1); level_ = lexical_cast_default<int>(cfg["level"], 0); movement_ = lexical_cast_default<int>(cfg["movement"], 1); Index: src/gamestatus.hpp =================================================================== --- src/gamestatus.hpp (revision 33970) +++ src/gamestatus.hpp (working copy) @@ -120,7 +120,7 @@ // Variable access - t_string& get_variable(const std::string& varname); + config::proxy_string get_variable(const std::string& varname); virtual const 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); @@ -165,6 +165,7 @@ rand_rng::simple_rng rng_ ; config variables; mutable config temporaries; // lengths of arrays, etc. + mutable string_map save_t_string; const rand_rng::set_random_generator generator_setter; /**< Make sure that rng is initialized first */ friend struct variable_info; }; Index: src/variable.cpp =================================================================== --- src/variable.cpp (revision 33970) +++ src/variable.cpp (working copy) @@ -713,7 +713,7 @@ } } -t_string& variable_info::as_scalar() { +config::proxy_string variable_info::as_scalar() { assert(is_valid); return (*vars)[key]; } Index: src/multiplayer_connect.cpp =================================================================== --- src/multiplayer_connect.cpp (revision 33970) +++ src/multiplayer_connect.cpp (working copy) @@ -1439,8 +1439,8 @@ 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); @@ -1465,7 +1465,7 @@ 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;