ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
I'm looking for a robust, flexible way of reading a configuration file into my C++ program. By "reading a configuration file" I mean taking a file with bash-like variable syntax
Code:
foo=bar
and assigning the value bar to a C++ variable associated with the string "foo".
I know about popt(3) which reads in command line parameters, and this is heart-stoppingly close to what I want - I just don't want to have to code and debug the wrapper that turns bash-like variables into a command-line string. Or write a messy script that turns the variables into a command-line string.
I'm not fussy about config file formats though - I'd be perfectly happy to use something like the Apache config file, but I must be able to read in all C++ builtin data types.
Have you tried rolling your own using C++? I'd be curious to see just how messy it comes out to be. I wrote my own for a small program I'm working on, and it's pretty kludgey waiting for me to improve it.
Ok, well, this is probably way longer than it needs to be, but here's my program's code for parsing its config file. The file consists of lines containing one word, then a number (or three numbers) separated by spaces. Like this:
Code:
light_pos 60.0 0.0 60.0
eye_pos 50.0 10.0 11.0
looking_at 0.0 15.0 0.0
near 20.0
far 80.0
angular_view_width 45.0
Please note that this code is just a first try, and may even be the wrong approach for this. If anyone sees a better way to do this with C++, please let me know.
Code:
void Viewing_geometry::read_in_viewing_params( string file_name )
{
ifstream config_file( file_name.c_str() );
string line; // We'll be reading in one line at a time,
string temp_word; // examining 1st word in that line.
string temp_ascii_double; // This is for converting the numerical values to doubles.
vector<double> vec; // We store the parameter's values in this vector.
double temp_double;
// Possible number separators are space or tab.
const string delims( " \t" );
// All the possible params that may be in the config.txt file, *in order*.
vector<string> params;
params.push_back( "light_pos" );
params.push_back( "eye_pos" );
params.push_back( "looking_at" );
params.push_back( "near" );
params.push_back( "far" );
params.push_back( "angular_view_width" );
while ( getline ( config_file, line ) ) // One line, 1 viewing parameter.
{
// Be sure to use string::size_type so we can safely compare to string:npos.
string::size_type beg_idx, end_idx;
beg_idx = line.find_first_not_of( delims ); // Starts at the beginning of the line.
end_idx = line.find_first_of( delims, beg_idx ); // getting the first word in the line.
if ( end_idx == string::npos ) // If we're on the last token in a line,
{ // set the end_idx appropriately
end_idx = line.length();
}
// Get the name of the parameter.
temp_word = line.substr( beg_idx, end_idx - beg_idx );
// Make sure it's on our list. XXX Maybe change this to use a std algorithm.
bool temp_word_found = false;
// Compare temp_word to each word in our list of viable ones.
for ( vector<string>::const_iterator it = params.begin();
it != params.end();
++it )
{
// TODO Add ablility to have comments in the file starting with #.
if ( temp_word == *it )
{
temp_word_found = true;
}
}
if ( !temp_word_found )
{
cout << "WARNING: Couldn't find parameter " << temp_word <<
". Moving on to next line in config.txt." << endl;
continue;
}
// Go on to the first number after the descriptive word.
beg_idx = line.find_first_not_of( delims, end_idx );
// Traverse the rest of line.
while ( beg_idx != string::npos )
{
end_idx = line.find_first_of( delims, beg_idx );
if ( end_idx == string::npos )
{
end_idx = line.length();
}
// Ok. Now: beg_idx points to the beginning of the substring,
// and end_idx points to one-past the end of it.
//temp_ascii_double = string( line, beg_idx, end_idx - beg_idx ); or
temp_ascii_double = line.substr( beg_idx, end_idx - beg_idx );
temp_double = strtod( temp_ascii_double.c_str(), NULL );
vec.push_back( temp_double );
beg_idx = line.find_first_not_of( delims, end_idx );
}
// Ok, vec is full. Figure out which param it corresponds to,
// and copy the values.
// I don't yet see a better way to do this... (?) XXX
if ( temp_word == "light_pos" )
{
//cout << "Setting light position: " <<
// vec[0] << " " << vec[1] << " " << vec[2] << endl;
d_light_pos.d_x = vec[0];
d_light_pos.d_y = vec[1];
d_light_pos.d_z = vec[2];
}
if ( temp_word == "eye_pos" )
{
//cout << "Setting eye position: " <<
// vec[0] << " " << vec[1] << " " << vec[2] << endl;
d_eye_pos.d_x = vec[0];
d_eye_pos.d_y = vec[1];
d_eye_pos.d_z = vec[2];
}
if ( temp_word == "looking_at" )
{
//cout << "Setting looking-at position: " <<
// vec[0] << " " << vec[1] << " " << vec[2] << endl;
d_looking_at.d_x = vec[0];
d_looking_at.d_y = vec[1];
d_looking_at.d_z = vec[2];
}
if ( temp_word == "near" )
{
//cout << "Setting near: " << vec[0] << endl;
d_near = vec[0];
}
if ( temp_word == "far" )
{
//cout << "Setting far: " << vec[0] << endl;
d_far = vec[0];
}
if ( temp_word == "angular_view_width" )
{
//cout << "Setting angular view width (in file): " << vec[0] << endl;
d_angular_view_width = vec[0];
// ...but don't forget to convert to radians, which we use
// exclusively in the code.
d_angular_view_width = d_angular_view_width * PI / 180.0;
cout << "and setting d_angular_view_width: " << d_angular_view_width << endl;
}
vec.clear();
// Now on to the next line.
}
}
If you use C++, take advantage of it and avoid the procedural style of code. I ran into the same problem with a command line analyzer (looked a lot like what you have as far as analysis), but later turned it into a parser class. This makes maintenance MUCH easier. Here is how I would go about processing:
1) declare separators (i.e. " " and "\t" general separation, "\n" statement separation, "#" comment, etc.)
2) parse the entire file into strings/statements with minimal whitespace
3) insert them into a list statement objects (a class you make up)
4) (maybe going too far...) create a list of patterns and corresponding function pointers to processing functions
5) register patterns/function pointers in an analyzer class that checks for patterns then dereferences the appropriate function pointer
6) submit the list of statements to the analyzer
This way, everything is generic with the exception of the patterns and the actions. This can be put in a single .cpp file that is nice and neat; only containing pattern strings and global functions for individual actions (all must be the same prototype though). The rest of the implementation is up to you as far as semantics, however it will be highly reusable.
It is interesting you brought that up, because I was thinking about how to implement a config file too. I think the absolute easiest way to do it is to parse and view things as objects as much as possible. I think I'll go home and try to work something out tonight and I'll post the general concept I come up with if you want.
ta0kira
I finally got started on it a few days ago. It looks like it will be too large to post here, but I can post the prototypes and descriptions when I am done.
ta0kira
I finally got started on it a few days ago. It looks like it will be too large to post here, but I can post the prototypes and descriptions when I am done.
ta0kira
Look into Boost Program Options. It'll parse command line options and simple key-value config files, and you get a nice --help message for pretty much free.
Wow, I don't recall exactly the context of this thread (not sure what I was working on 5 years ago); however, I did write a config parser several years ago that I use in a few projects. It works very well for me, although it might not be appropriate for your application. It isn't a stand-alone lib; however, it's contained in 1 header and one source (it would need a few minor changes to be separated from the project.) It's extremely simple in that every line is tokenized based on whitespace, command substitution is allowed, and the rest is up to the program to interpret. I treat the first token as a command and the rest of the line as its options. http://rservr.berlios.de/user/assembly.html#UCFF http://rservr.berlios.de/app/client-...on.html#BiT_CP http://svn.berlios.de/wsvn/rservr/tr...onfig-parser.h http://svn.berlios.de/wsvn/rservr/tr...fig-parser.cpp
As I said above, this isn't a stand-alone parser at the moment, but it's success as the file parser for two of my largest projects (my actual parser project not included) makes me wonder if it would be appropriate as its own project. What's nice is the actual interpreter can be painfully simple (as it is in its native project,) or it can be very complex, e.g. allowing additional syntax and sematics (as it is in a privately-funded project of mine.)
Kevin Barry
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.