Elegant way of returning possibly non-existant values..

Started by
10 comments, last by sb01234 14 years, 10 months ago
I'm using an std::map of boost:anys to store configuration variables for my game. The function prototypes should give an idea of what I am doing:

template <class T>
int variableExists(const string& variableName); // Returns -1 if the variable doesn't exist, 0 if it exists and is not of type T, 1 if it exists and is of type T.

template <class T>
void setVariable(const string& variableName, T value); // Sets the variable to the value.

template <class T>
T getVariable(const string& variableName); // Returns the value of the variable if it exists.


The problem is that in getVariable(), the given variable might not exist, or might not be of that type. For example:

setVariable<int>("test_int", 23);
int test = getVariable<int>("not_test_int"); // not_test_int does not exist, what to return?


I implemented a simple template class that contains a value of type T and a boolean stating whether or not the value is valid. This works, but I'm interested if there are more elegant ways of doing this, or if any other structures exist in the std or boost library that may help. Thanks
Advertisement
Have you tried boost::optional? Or std::auto_ptr<T> and use NULL to indicate non existance.
I would combine your variableExists and getVariable into one function to get the variable.
// Returns -1 if the variable doesn't exist// 0 if it exists and is not of type T// 1 if it exists and is of type T.// If 1 is returned, you may safely use the contents of 'value'.// Otherwise, the contents are undefined.template <class T>int getVariable(const string& variableName, T & value);


Or, I forgot about this approach as well, if you want to switch things around:
// isValid stores the result of the operating.// 0 if it exists and is not of type T// 1 if it exists and is of type T.// If isValid is 1, you may safely use the contents returned.// Otherwise, the contents are undefined.template <class T>T getVariable(const string& variableName, bool & isValid);
I think boost::optional is exactly what I'm looking for. Thanks.
emplate <class T>T getVariable(const string& variableName, T default_value) {  if (exists(variableName) {    return get(variableName)  } else {    return default_value;  }}


However, this is an answer to a possibly incorrect question.

Assume you have server configuration file, with the following line:
port=2345

What all can go wrong? What do you expect the result to be.
- port entry is missing. Does it make sense to continue, or use a default value? Usually, no, throw an exception
- port value is unparsable. Assume a default value? Generally no, typos happen, and admin will be wondering why the server is running, but cannot be contacted. Again, throw exception.
- port value is unexpected (1234567). This is logical error, but not something configuration can determine by itself. Return, let caller work it out.

- Optionally, implement parsing as part of type, put default value in there, in case it is unable to parse, or logical error occurs, throw an exception.

IMHO, if an optional field is missing, provide default value.
If a required field is missing, throw an exception.



Also - there is boost::program_options, which solves all of the above problems already.
Quote:Original post by Drew_Benton
I would combine your variableExists and getVariable into one function to get the variable.
*** Source Snippet Removed ***


On the other hand... this method is quite slick. I will implement both and see how I like it.

Thanks for the replies.
bool variableExists(const string& variableName); template <class T>bool variableMatchesType( const string &variableName );template <class T>void setVariable(const string& variableName, T value); // Sets the variable to the value.template <class T>T getVariable(const string& variableName); // Returns the value of the variable if it exists, exception if it doesn't.// ORtemplate <class T>*T getVariable(const string& variableName); // Returns the value of the variable if it exists, null if it doesn't.// ORtemplate <class T>&T getVariable(const string& variableName, const T& defaultValue); // Returns the value of the variable if it exists, default if it doesn't.


[edit: personally I'd err towards exceptions. You're not using C, do it right.]
Quote:Original post by Drew_Benton
Or, I forgot about this approach as well, if you want to switch things around:
*** Source Snippet Removed ***


I prefer the first one you mention, because I like the syntax of using it.

int test_value;if (getVariable("test_value", &test_value)) {    cout << "test_value: " << test_value << endl;} else {    // Throw exception or use default value.}
I've never really used exceptions in C++. Out of curiosity, is this what you guys have in mind when you suggest to use exceptions?

template <class T>T getVariable(const string& variableName) throw(runtime_error) {    if (variableDoesNotExistOrIsWrongType)        throw(runtime_error("Wrong type or doesn't exist"));    else         return value;}int value;try {    value = getVariable("test_int");} catch (runtime_error& e) {    // Deal with error.}
Quote:Original post by c_olin
I've never really used exceptions in C++. Out of curiosity, is this what you guys have in mind when you suggest to use exceptions?

*** Source Snippet Removed ***


Something like that, except you should probably derive your own type from std::runtime_error so that you can distinguish between other types of exceptions.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

This topic is closed to new replies.

Advertisement