Storing game settings and variables

Started by
12 comments, last by screwtape 18 years, 10 months ago
I'll start by explaining the situation, essentially in my engine I want to have a system much like in the Quake games where you can store variables and retreive variables of either string, integer or float data types easily. I want this all to be nice and tidy in a single object with each variable having some degree of organisation, for example variables relating to system settings can be flagged as system settings and these variables might be loaded on startup, for example they maybe read in from a text based settings file, the kind of setting I'm talking about might be a variable "resolutionx" being set to the integer value 640. Also I might want game variables, for example "playername" set to a string value of "Xest" and this could be set via the console with a command such as: set playername Xest Now I have a pretty good picture of what I want in my head but the problem is how to code it, I'm an old style C programmer and have to admit I'm not totally to grips with C++ such as the limitations of templates and such. I'd like the interface to the class toring the settings to be nice and tidy, for example I could have int fnRetreiveValueAsInt(char *VariableName) and then have one for retreiving values as a string and such, but that's not what I want, I want to know if it's possible to have a single retreive value function that will work for all variable types, and whether this could lead to any potential problems, using polymorphism, could I not have: bool RetreiveValue(char *VariableName, int *VariableValue); bool RetreiveValue(char *VariableName, char *VariableValue); bool RetreiveValue(char *VariableName, float *VariableValue); Then simply return the value back to the variable passed by the pointer in the second argument depending on what type of pointer was passed, and, for error checking, use the boolean return value to declare whether the operation was a success? For example, if an int pointer is passed to retreive the value of a variable that's actually stored in the class as a char it would simply return false such that it couldn't perform a valid conversion then perform error checking on the boolean value at the function call itself for each variable lookup? As for actually storing the variables in the Settings class itself, I'm guessing I'd want a vector(Is a vector the best bet?) of templates with things like the following: string variablename; templatetype variablevalue; int variabletype; // i.e. SYSTEM_VARIABLE, GAME_VARIABLE etc. As I say I'm a little rusty with programming in general and my knowledge of C++ and templates isn't that strong, neither is my knowledge of things like RTTI (Is RTTI something that'd help me here?). Am I on the right tracks or is this system flawed in some way? Can someone recommend a better/safer/cleaner method than this? Essentially the class needs to be responsible merely for retreiving and saving variables of different types and the variables maybe added via a variety of methods - settings files at startup, game code at run time, player input via console and such. Any help, comments, ideas much appreciated, I think sorting this out will answer a lot of questions I have about templates and polymorphism, including what they can and can't do and such.
Advertisement
Two words (four if you include the namespaces):
std::string. boost::variant.

Enigma
I store all this kind of stuff in LUA scripts - its a bit of a leap but once its all working nicely its a breeze...
-Scoot
If you're focusing on the Win32 platform, you might look into GetPrivateProfileString()/WritePrivateProfileString(). They are deprecated in favor of the registry, but they aren't going anywhere. These store and retrieve all data in char* strings. You can atoi() and atof() the data to ints and floats as you need them.
Not really interested in dropping it out to scripting, it's a bit overkill for what I need. I'm not terribly keen on boost as that's not helping me solve my problem so I'll still be lacking as much of a clue as I am now and using Win32 SDK functions is out of the question as I can already do that and it doesn't give me the generic setup I'm aiming for whilst also tying me to the Win32 platform. Thanks for the advice, but was looking for something relating to my original plans I laid out and whether my theory setup is acheivable or whether it needs to be reworked completely.
boost does help you solve the problem, and do so in a much cleaner and safer manner. Read the documentation.

Example:
#include <iostream>#include <map>#include <string>#include <sstream>#include "boost/variant.hpp"class GetInteger	:	public boost::static_visitor< int >{	public:		int operator()(int i)		{			return i;		}		int operator()(float f)		{			return f;		}		int operator()(std::string string)		{			throw std::string("Cannot convert string to int");		}};class GetString	:	public boost::static_visitor< std::string >{	public:		std::string operator()(int i)		{			std::stringstream ss;			ss << i;			return ss.str();		}		std::string operator()(float f)		{			std::stringstream ss;			ss << f;			return ss.str();		}		std::string operator()(std::string string)		{			return string;		}};std::map< std::string, boost::variant< int, float, std::string > > variables;int main(){	variables.insert(std::make_pair("resolutionx", 640));	variables.insert(std::make_pair("resolutiony", 480.0f));	variables.insert(std::make_pair("colourdepth", "32 bit"));	variables.insert(std::make_pair("playername", "Xest"));	variables.insert(std::make_pair("playerrace", 32));	try	{		std::cout << "resolutionx: " << boost::apply_visitor(GetInteger(), variables["resolutionx"]) << '\n';	}	catch (std::string & exception)	{		std::cout << "Error reading resolutionx: " << exception << '\n';	}	try	{		std::cout << "resolutiony: " << boost::apply_visitor(GetInteger(), variables["resolutiony"]) << '\n';	}	catch (std::string & exception)	{		std::cout << "Error reading resolutiony: " << exception << '\n';	}	try	{		std::cout << "colourdepth: " << boost::apply_visitor(GetInteger(), variables["colourdepth"]) << '\n';	}	catch (std::string & exception)	{		std::cout << "Error reading colourdepth: " << exception << '\n';	}	try	{		std::cout << "playername: " << boost::apply_visitor(GetString(), variables["playername"]) << '\n';	}	catch (std::string & exception)	{		std::cout << "Error reading playername: " << exception << '\n';	}	try	{		std::cout << "playerrace: " << boost::apply_visitor(GetString(), variables["playerrace"]) << '\n';	}	catch (std::string & exception)	{		std::cout << "Error reading playerrace: " << exception << '\n';	}}

Of course, you could write your visitors to disallow int to string conversions, or to parse numerical strings for string to int/float conversion etc.

Enigma
Quote:Original post by Xest
...and using Win32 SDK functions is out of the question as I can already do that and it doesn't give me the generic setup I'm aiming for whilst also tying me to the Win32 platform....

Fair enough. I wasn't sure if you were concerned with that or not. I'd google for things like "ini file parser", "config file parser", and "key-value file parser." Those returned a good number of hits, and you could at least get a look at some code aimed at a similar purpose.

If I were doing this, I'd probably save to a text file, and just use strings, relying on sprintf or stringstream to convert number vars to text and atoi and atof to convert back. Boost would be nicer, but it's by no means necessary. I'm not sure if I'd convert to numbers in the Get- functions, or if I'd just return a string and have the app convert based on the type of setting it's trying to fill.

how come you dont want to do it the c-way with fnRetreiveValueAsInt?
you know just because you _can_ do something oop it doesnt have to mean you _should_ do it oop. the easiest, working, solution is often best. though sometimes its not, for example if you want to create something thats more reusable or more extendable.
now if you still want to do it the oop way, id say (as the ones before me): stl and boost is your best friends.
----------Thale Cres
This actually came up recently here, in general programming [well, not exactly, but the solution can be easily adapted to this scenario] and is far more re-usable and extensable than the C-style.

Though might not be so good to learn templating and polymorphism from.
"boost does help you solve the problem, and do so in a much cleaner and safer manner. Read the documentation."

Yes, but it doesn't help with my understanding of what templates and such can and can't do and how to work with them properly which really doesn't help me become a better programmer for using it. Whereas I don't fancy writing my own graphics library and hence would just use OpenGL/Direct3D I feel something like this is something I should be capable of writing a home grown solution for without needing to resort to 3rd party libraries to do everything for me. This isn't so much a question of just getting it done, but more a question of understanding how to get it done. That said, I do appreciate your reply a great deal, that code snippet alone is a very nice example if I do wish to switch to boost when I've got a better understanding of C++, so thank you for that ;)

"Fair enough. I wasn't sure if you were concerned with that or not. I'd google for things like "ini file parser", "config file parser", and "key-value file parser." Those returned a good number of hits, and you could at least get a look at some code aimed at a similar purpose."

That's not so much the issue I'm struggling with, I can read and write files fine, the problem is to do with in memory storage and a generic method of accessing different variable types in memory.

"how come you dont want to do it the c-way with fnRetreiveValueAsInt?
you know just because you _can_ do something oop it doesnt have to mean you _should_ do it oop. the easiest, working, solution is often best. though sometimes its not, for example if you want to create something thats more reusable or more extendable.
now if you still want to do it the oop way, id say (as the ones before me): stl and boost is your best friends."

I'd like to try and maintain extensibility without having to modify the interface if possible, perhaps I'm being too picky here with what I want but it just seems a lot tidier to have a single function in the interface to do the job than to have to keep a function for every data type I decide to work with.

I don't mean to sound un-appreciative of the help but I'm trying to gain a specific understanding and if the reason I'm being given work arounds is because what I want to do is too difficult, flawed or some such than I'd rather be just flat out told that, if not I'd just like some kind of confirmation as to whether my solution is workable and what needs changing if anything ;) Thanks for the info so far though all ;)

This topic is closed to new replies.

Advertisement