The cConfig class stores the values in genesis throughout a run. It is defined in the files "config.hh" and "config.cc" in the directory "current/source/main/". This is one of the rare classes that has a longer class definition in the header file than that of its individual methods in the code file. This is because so many methods are accessor functions that are defined as they are declared.
Currently, this is a static class. That means there can never be an object of type cConfig; there is only the class itself that exists. This comes from the idea that an avida run can only ever have a single configuration. However, ideally we should make it possible for multiple populations to co-exist, each with distinct configurations, so at some point in the future (when I get the energy, and don't have more pressing programming issues!) I will convert this to a normal class type.
Here is a heavily abridged version of the class definition (from "config.hh").
class cConfig { private: static cString event_filename; static cString analyze_filename; static int rand_seed; static double copy_mut_prob; // [ ...about 100 other variables stored... ] // A method that will process the command line arguments static void ProcessConfiguration(int argc, char * argv[], cGenesis & genesis); public: // A method that will setup everything in cConfig static void Setup(int argc, char * argv[]); // Accessors... static cString & GetEventFilename() { return event_filename; } static cString & GetAnalyzeFilename() { return analyze_filename; } static int GetRandSeed() { return rand_seed; } static double GetCopyMutProb() { return copy_mut_prob; } // [ ...about 100 other variable accessors... ] // Set Methods... static void SetEventFilename(const cString & in_name) { event_filename = in_name; } static void SetAnalyzeFilename(const cString & in_name) { analyze_filename = in_name; } // [ ...again, many more set methods... ] };
Note that there is no constructor or destructor since objects of cConfig are never created or destroyed. The Setup() method will, however, setup any values that need to be handled.
You will need to know about the code file for cConfig in order to make any additions to the class. The code file is broken up into three sections. The first is a list of all of the variables declared inside of the class definition. In order for a variable to be static, it has to be declared in both the class definition, and again outside the class in the code file. The entire beginning of the code file is in the form:
cString cConfig::event_filename; cString cConfig::analyze_filename; int cConfig::rand_seed; double cConfig::copy_mut_prob;
Remember that a header file can be included inside of many other files so that each of those know how to interact with this class. Well, the reason for this section of the code file is to make it clear to the compiler that this will be the main location to store the static variables associated with this class. Normally variables setup when an object is created; since there is no object, we must explicitly tell it to setup the variables.
After the long list of variable re-declarations, the next portion of the file is the Setup() method. Its body defines an object of class cGenesis, and then uses it to load all of the individual variables that make up cConfig. Most of this method consists of lines in the form:
event_filename = genesis.ReadString("EVENT_FILE", "events.cfg"); analyze_filename = genesis.ReadString("ANALYZE_FILE", "analyze.cfg"); world_x = genesis.ReadInt("WORLD-X"); world_y = genesis.ReadInt("WORLD-Y"); point_mut_prob = genesis.ReadFloat("POINT_MUT_PROB"); copy_mut_prob = genesis.ReadFloat("COPY_MUT_PROB");
The final portion of the config.cc file consists of the method ProcessConfiguration(), which handles the command line arguments entered by the user. This will, perhaps, be the topic of another lesson.
Now that we know how the cConfig class works, we need to be able to use it from the rest of avida whenever we need it. This is very simple; to call a method on a static class from anywhere, all you need to do is type the class name, a double colon ('::') and then the method name that you wish to call. For an example that we've already seen, remember that the body of all of the inject events contained the line:
if (fname == "START_CREATURE") fname = cConfig::GetStartCreature();
This line sets the inject organism filename to the one found in the cConfig class if the name was otherwise set to be "START_CREATURE". To do this, we need to ask cConfig what the starting creature filename is. Fortunately, that's an easy thing to do.
We finally have all of the information of how a variable in the genesis file is loaded, stored, and accessed when needed. However, this can be a lot to remember. Below is a checklist of all of the locations in the source code that you would need to make a change when you want to perform such a programming feat.
1. Add the variable to the private data of cConfig [in config.hh] : This is the location that the variable is defined and stored over the course of an avida run.
2. Add and accessor to the new data [config.hh] : All variables must have a method that makes them accessible from the rest of the source code.
3. Add a method to set a new value to the data [config.hh] : This is optional. If the new variable can ever change during a run, there must be a method in cConfig that causes that change.
4. List the variable in the code file. [config.cc] : All variables must be listed a second time at the top of config.cc so that the single instance of them is actually created.
5. Load the variable in cConfig::Setup() [config.cc] : This is where the value that is stored in the genesis configuration file initializes the variable you've just added to cConfig. Remember to provide a default value in case the user leaves it out.
6. Use the variable wherever you need it! :
Your new variable is ready for you. Use it wisely.