Search code examples
c++exceptionboostboost-program-options

Boost 1.55.0 Program Options Issues


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);
    }
}

Solution

  • 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.