Reputation: 680
Decided to finally bite the bullet and download, build, and link in Boost after having failed to find a reasonable ini file reading method over the last few months.
After creating a test project to see how the process worked I am stuck with the program throwing an unknown option exception on one of the options. Here is the output of the test program along with the caught error message (added in a try/catch for proper error handling) as well as the ini file contents and the full code:
Testing ini reading....
Press any key to continue . . .
unrecognised option 'PLAYERDATA.ID'
Loaded values:
Name:
ID: 8639072
gDiff:
Game Difficulty Values:
Encounter Randomness: 5.96783e-039
Player Damage Modifier: 1.20858e-038
Computer Damage Modifier: 127232
Item Range Low: -2
Item Range High: 8623500
Press any key to continue . . .
settings.ini:
[PLAYERDATA]
NAME = Game1A_g
ID = 12231
GDIFF = Medium
#Difficulty Values
[EASY]
ERAND = 0.25f
PDMOD = 2.12f
CDMOD = 0.65f
IRL = 1
IRH = 10
[MEDIUM]
ERAND = 1.00f
PDMOD = 1.00f
CDMOD = 1.00f
IRL = 1
IRH = 25
[HARD]
ERAND = 1.35f
PDMOD = 0.75f
CDMOD = 1.50f
IRL = 1
IRH = 30
Code:
#include <iostream>
#include <Windows.h>
#include <string>
#include <fstream>
#include "boost/program_options.hpp"
using std::cout;
using std::string;
using std::endl;
using std::ifstream;
namespace gOp = boost::program_options;
struct prgVars
{
string name;
int id;
string gDiff;
float encRand;
float pDMod;
float cDMod;
int iRLow;
int iRHigh;
};
void loadGData(prgVars& pgv);
int main()
{
prgVars pgv;
cout << "Testing ini reading...." << endl;
system("Pause");
loadGData(pgv);
cout << "Loaded values:" << endl;
cout << "Name: " << pgv.name << endl;
cout << "ID: " << pgv.id << endl;
cout << "gDiff: " << pgv.gDiff << endl << endl;
cout << "Game Difficulty Values: " << endl;
cout << "Encounter Randomness: " << pgv.encRand << endl;
cout << "Player Damage Modifier: " << pgv.pDMod << endl;
cout << "Computer Damage Modifier: " << pgv.cDMod << endl;
cout << "Item Range Low: " << pgv.iRLow << endl;
cout << "Item Range High: " << pgv.iRHigh << endl;
system("Pause");
}
void loadGData(prgVars& pgv)
{
gOp::options_description desc("Settings");
desc.add_options()
("PLAYERDATA.NAME",gOp::value<string>(&pgv.name),"player_name"),
("PLAYERDATA.ID",gOp::value<int>(&pgv.id),"player_id"),
("PLAYERDATA.GDIFF",gOp::value<string>(&pgv.gDiff),"game_difficulty");
gOp::variables_map vm;
try
{
ifstream settings_file("settings.ini",ifstream::in);
gOp::store(gOp::parse_config_file(settings_file, desc), vm);
settings_file.close();
gOp::notify(vm);
}
catch (const gOp::error& e )
{
std::cerr << e.what() << std::endl;
}
if (pgv.gDiff == "Easy")
{
gOp::options_description desc("DVars");
desc.add_options()
("EASY.ERAND",gOp::value<float>(&pgv.encRand),"encounter_rate"),
("EASY.PDMOD",gOp::value<float>(&pgv.pDMod),"player_damage_bonus"),
("EASY.CDMOD",gOp::value<float>(&pgv.cDMod),"computer_damage_bonus"),
("EASY.IRL",gOp::value<int>(&pgv.iRLow),"item_rng_low"),
("EASY.IRH",gOp::value<int>(&pgv.iRHigh),"item_rng_high");
gOp::variables_map vm;
ifstream settings_file("settings.ini",ifstream::in);
gOp::store(gOp::parse_config_file(settings_file, desc), vm);
settings_file.close();
gOp::notify(vm);
}
else if (pgv.gDiff == "Medium")
{
gOp::options_description desc("DVars");
desc.add_options()
("MEDIUM.ERAND",gOp::value<float>(&pgv.encRand),"encounter_rate"),
("MEDIUM.PDMOD",gOp::value<float>(&pgv.pDMod),"player_damage_bonus"),
("MEDIUM.CDMOD",gOp::value<float>(&pgv.cDMod),"computer_damage_bonus"),
("MEDIUM.IRL",gOp::value<int>(&pgv.iRLow),"item_rng_low"),
("MEDIUM.IRH",gOp::value<int>(&pgv.iRHigh),"item_rng_high");
gOp::variables_map vm;
ifstream settings_file("settings.ini",ifstream::in);
gOp::store(gOp::parse_config_file(settings_file, desc), vm);
settings_file.close();
gOp::notify(vm);
}
else if (pgv.gDiff == "Hard")
{
gOp::options_description desc("DVars");
desc.add_options()
("HARD.ERAND",gOp::value<float>(&pgv.encRand),"encounter_rate"),
("HARD.PDMOD",gOp::value<float>(&pgv.pDMod),"player_damage_bonus"),
("HARD.CDMOD",gOp::value<float>(&pgv.cDMod),"computer_damage_bonus"),
("HARD.IRL",gOp::value<int>(&pgv.iRLow),"item_rng_low"),
("HARD.IRH",gOp::value<int>(&pgv.iRHigh),"item_rng_high");
gOp::variables_map vm;
ifstream settings_file("settings.ini",ifstream::in);
gOp::store(gOp::parse_config_file(settings_file, desc), vm);
settings_file.close();
gOp::notify(vm);
}
}
Upvotes: 1
Views: 621
Reputation: 680
So I managed to figure this out with some help from sehe's post and T.C.'s comment. While the added commas were a problem the main issue was that I did not really understand how the parser read the file. It reads the whole thing at once, not just particular sections of it based on the pair identifier given in the first parameter of add_options().
So I went through and made an element for each value in the ini even if I was not going to eventually use it and updated the first options list to look like this:
gOp::options_description desc("Settings");
desc.add_options()
("PLAYERDATA.NAME",gOp::value<string>(&cName),"player_name")
("PLAYERDATA.ID",gOp::value<int>(&pgv.id),"player_id")
("PLAYERDATA.GDIFF",gOp::value<string>(&pgv.gDiff),"game_difficulty")
("EASY.ERAND",gOp::value<float>(&pgv.encRand_e),"encounter_rate")
("EASY.PDMOD",gOp::value<float>(&pgv.pDMod_e),"player_damage_bonus")
("EASY.CDMOD",gOp::value<float>(&pgv.cDMod_e),"computer_damage_bonus")
("EASY.IRL",gOp::value<int>(&pgv.iRLow_e),"item_rng_low")
("EASY.IRH",gOp::value<int>(&pgv.iRHigh_e),"item_rng_high")
("MEDIUM.ERAND",gOp::value<float>(&pgv.encRand_m),"encounter_rate")
("MEDIUM.PDMOD",gOp::value<float>(&pgv.pDMod_m),"player_damage_bonus")
("MEDIUM.CDMOD",gOp::value<float>(&pgv.cDMod_m),"computer_damage_bonus")
("MEDIUM.IRL",gOp::value<int>(&pgv.iRLow_m),"item_rng_low")
("MEDIUM.IRH",gOp::value<int>(&pgv.iRHigh_m),"item_rng_high")
("HARD.ERAND",gOp::value<float>(&pgv.encRand_h),"encounter_rate")
("HARD.PDMOD",gOp::value<float>(&pgv.pDMod_h),"player_damage_bonus")
("HARD.CDMOD",gOp::value<float>(&pgv.cDMod_h),"computer_damage_bonus")
("HARD.IRL",gOp::value<int>(&pgv.iRLow_h),"item_rng_low")
("HARD.IRH",gOp::value<int>(&pgv.iRHigh_h),"item_rng_high");
And then removed all of the other code from the function after the try/catch. That is instead now used in main to branch between each difficulty and then cout the values for the related variables.
The output is now showing correctly:
Testing ini reading....
Press any key to continue . . .
Game1A_g
Loaded values:
Name: Game1A_g
ID: 12231
gDiff: Medium
Game Difficulty Values:
Encounter Randomness: 1
Player Damage Modifier: 1
Computer Damage Modifier: 1
Item Range Low: 1
Item Range High: 25
Press any key to continue . . .
So, in summary you have to include each ini option in your add_options() list during the first such list or else you will have the same issues I did with the program throwing unknown option errors and not getting the data from the file or use the bool parameter of parse_config_file (use true to ignore unknown options).
Upvotes: 0
Reputation: 392833
You have too many commas:
desc.add_options()
("PLAYERDATA.NAME" , gOp::value<string>(&pgv.name) , "player_name") /*HERE*/
("PLAYERDATA.ID" , gOp::value<int>(&pgv.id) , "player_id") /*HERE*/
("PLAYERDATA.GDIFF", gOp::value<string>(&pgv.gDiff), "game_difficulty");
Note that if you compile at sufficiently high warning levels, your compiler will tell you about this (in a very cryptic fashion...).
Here's a fixed program: Live On Coliru
Output:
Testing ini reading....
unrecognised option 'EASY.ERAND'
Loaded values:
Name:
ID: 4240909
gDiff:
Game Difficulty Values:
Encounter Randomness: 5.94265e-39
Player Damage Modifier: 0
Computer Damage Modifier: 0
Item Range Low: 0
Item Range High: 4215760
Note that you might want to look at Boost PropertyTree for parsing INI files too.
Upvotes: 2