Using a Class Object to Help Read a Control File
One thing we’re used to in travel modeling is control files. Â It seems to harken back to the days of TranPlan where everything had a control file to control the steps.
In my case, I have a control file for my nested logit mode choice program, and because of the age of the mode choice program, I want to redesign it. Â The first part of this is reading the control file, and I did a little trick to help with reading each control file line. Â With C++, there is no way to read variables in from a file (like there is with FORTRAN).
The first part of the code reads the control file, and you will see that once I open and read the control file, I section it out (the control file has sections for files ($FILES), operation parameters ($PARAMS), operation options ($OPTIONS), and mode choice parameters ($PARMS). Each section ends with an end tag ($END). This adds the flexibility of being able to re-use variables in different locations.
After the section, the next portion of the code reads the line and checks to see if FPERIN is found. If it is, a ControlFileEntry object is created. This object is a class that is used to return the filename held in the object. This makes it easy to reduce code.
int readControlFile(char *controlFileName){ cout << "Reading " << controlFileName << endl; //Read the control file string line; bool inFiles=false, inParams=false, inOptions=false, inParms=false; ifstream controlFile(controlFileName); if(!controlFile.good()){ cout << "PROBLEMS READING CONTROL FILE" << endl; return 1; } while(controlFile.good()){ getline(controlFile,line); //check the vars sections if(line.find("$FILES")!=string::npos) inFiles=true; if(line.find("$PARAMS")!=string::npos) inParams=true; if(line.find("$OPTIONS")!=string::npos) inOptions=true; if(line.find("$PARMS")!=string::npos) inParms=true; if(line.find("$END")!=string::npos){ inFiles=false; inParams=false; inOptions=false; inParms=false; } if(inFiles){ cout << "Checking files" << endl; if(line.find("FPERIN")!=string::npos){ controlFileEntry cfe(line); PerTrpIn=cfe.filename; } //SNIP!!! return 0; }
The controlFileEntry is code is below. Â This is used at the top of the code, just below the preprocessor directives (the #include stuff).
class controlFileEntry{ public: string filename; controlFileEntry(string Entry){ beg=(int)Entry.find("=")+2; end=(int)Entry.rfind("\'")-beg; filename=Entry.substr(beg,end); } ~controlFileEntry(){ beg=0; end=0; filename=""; } private: string Entry; int beg; int end; };
The class has one public member, filename, which is what is read in the code where it is used. There are two public functions. The first is the constructor (controlFileEntry) which is used when creating the object. The second is the de-constructor (~controlFileEntry), which sets the beg, end, and filename variables to zero and blank. Â The beg, end (misnomer), and the line sent to it are private and cannot be used in code.
This can be extended, as the file entry type is fine when there are quotes around the item (it is setup for that, note the -2 in beg). Â I wrote a different one for integers, floating point, and boolean values.
class controlParamEntry{ public: int ivalue; bool bvalue; double dvalue; controlParamEntry(string Entry){ beg=(int)Entry.find("=")+1; end=(int)Entry.rfind(",")-beg; ivalue=0; dvalue=0; if(Entry.substr(beg,end)=="T"){ bvalue=true; ivalue=1; dvalue=1; }else if(Entry.substr(beg,end)=="F"){ bvalue=false; ivalue=0; dvalue=0; } if(ivalue==0){ ivalue=atoi(Entry.substr(beg,end).c_str()); } if(dvalue==0){ dvalue=atof(Entry.substr(beg,end).c_str()); } } ~controlParamEntry(){ beg=0; end=0; ivalue=0; dvalue=0; Entry=""; } private: string Entry; int beg; int end; };
As you can see above, there are return values for floating point (dvalue), integer (ivalue), and boolean (bvalue).
Tune in next week to see more of the code.
Tags: c++, cube, mode-choice, nested-logit, voyager