Coke with lime = amazing

posted in Programmology
Published November 07, 2005
Advertisement

Alrighty, I'm going to code again. It's just a little hard to have a programming job and still be motivated to code in my free time. But I'm interested in graphics enough to get back into it. That, and I like C++, and at work I do mostly shell/php/perl. I miss compiled languages.

I worked on how I want to incorporate settings into my "engine." I put that in quotes because that makes it seem like such a large-scale project, when this is really just a little machine to help me learn design and advanced rendering techniques.

I initially wrote a settings class that parses a file and stores a vector of name/value pairs (of type string/string). Then I created one instance of this class as a global object. As I thought about it and roughly sketched out how I would use this, I realized how bad this design was.

My first improvement was to store the value of each setting as boost::any instead of a string (which was lexical-casted to convert into appropriate type). boost::any gives me greater flexibility in the types of values stored and is also faster. I have a templated "GetSetting" function that performs an any_cast on the value to the requested type.

I'm still having some problems with casting though. With boost::any, if you don't any_cast the value back to the original type, it throws an exception (even if the value is an int and you request a long, for example). This was annoying because if a value is an int but I wanted a long, I'd have to do:

(long)settings->GetSetting("width");

I could add code to use typeid() and detect the requested type and do all the conversion for me, but that means I would have to enable RTTI. Is it worth it? Honestly I don't know how RTTI affects the performance.

Because this isn't a huge deal, I don't think I really care about it. This still lets me the configure the system in a pretty flexible way, especially with my second improvement.

My second improvement involved the scope of the settings object. More and more I'm realizing how to avoid those global objects, and even how global objects indicates bad design. Why do I need to store settings for the renderer in the same place as settings for my logger? I used to have one long ValidateSettings function that would make sure everything in there is in tact. When the renderer is initializing it trusts everything in the global settings object. Ew, we have objects relying on this global object declared somewhere else, etc, etc.

I ended up putting a Settings object as a public member of any class that I wanted configurable. So the renderer has an object called "Settings" and to configure it, you actually call, for example: Renderer->Settings->SetSetting("FOV", 90.0f);

This can be done for the logger, renderer, kernel, etc. For any object that I want to configure. Whenever these objects initialize, it calls its own ValidateSettings function which makes sure all the settings that object needs are in there and valid.

Before it does that though, it parses the settings file. I like this part. Every settings object parses the same settings file, but retrieves whatever portion it needs. The settings file is broken into sections, indicated by a [header] then a list of name/value pairs, and when a Settings object is fired to parse the file, it requests a section name to parse. So an example file would look like:

[renderer]
deviceType=hal
autogenMipMaps=1
useSpecularLighting=1
...

[window]
windowedWidth=563
windowedHeight=335
fullscreen=0
...

[logger]
logApplicationMessages=1
logSystemMessages=0
...
0 likes 1 comments

Comments

Gaheris
How about implementing arrays (or lists) as possible values? Simply serialize them using a delimeter character should do the trick.
November 09, 2005 01:29 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement