【问题标题】:Boost 1.55.0 Program Options IssuesBoost 1.55.0 程序选项问题
【发布时间】:2014-05-17 12:04:37
【问题描述】:

在过去几个月没有找到合理的ini文件读取方法后,最终决定硬着头皮在Boost中下载、构建和链接。

在创建一个测试项目以查看该过程的工作方式后,我遇到了程序在其中一个选项上引发未知选项异常的问题。这是测试程序的输出以及捕获的错误消息(添加到 try/catch 中以进行正确的错误处理)以及 ini 文件内容和完整代码:

测试 ini 阅读....

按任意键继续。 . .

无法识别的选项“PLAYERDATA.ID”

加载值:

姓名:

ID:8639072

gDiff:

游戏难度值:

遭遇随机性:5.96783e-039

玩家伤害修正:1.20858e-038

计算机损坏修正:127232

项目范围低:-2

物品范围高:8623500

按任意键继续。 . .

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

代码:

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

【问题讨论】:

    标签: c++ exception boost boost-program-options


    【解决方案1】:

    你的逗号太多了:

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

    请注意,如果您以足够高的警告级别进行编译,您的编译器会告诉您这一点(以一种非常神秘的方式......)。

    这是一个固定的程序:Live On Coliru

    输出:

    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
    

    请注意,您可能还想查看 Boost PropertyTree 来解析 INI 文件。

    【讨论】:

    • 感谢您的回复,因为它帮助我找到了除额外逗号之外的主要问题。我没有考虑每个存在的 ini 选项,因此当它通过 ini 文件时,它在第一个找不到的选项上窒息。我会在我的回答中更好地解释。
    • 这仍然是关闭的,因为parse_config_file 在看到一个它无法识别的选项时会默认抛出,这是你不想要的。您希望所有 parse_config_file(settings_file, desc) 调用都是 parse_config_file(settings_file, desc, true)
    【解决方案2】:

    所以我在 sehe 的帖子和 T.C. 的评论的帮助下设法解决了这个问题。虽然添加的逗号是一个问题,但主要问题是我并不真正理解解析器是如何读取文件的。它一次读取整个内容,而不仅仅是基于 add_options() 的第一个参数中给出的对标识符的特定部分。

    因此,即使我最终不打算使用它,我也为 ini 中的每个值创建了一个元素,并将第一个选项列表更新为如下所示:

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

    然后在 try/catch 之后从函数中删除所有其他代码。现在,它在 main 中用于在每个难度之间进行分支,然后计算相关变量的值。

    现在可以正确显示输出:

    测试 ini 阅读....

    按任意键继续。 . .

    Game1A_g

    加载值:

    名称:Game1A_g

    ID:12231

    gDiff:中等

    游戏难度值:

    遭遇随机性:1

    玩家伤害修正:1

    电脑伤害修正:1

    项目范围低:1

    物品范围上限:25

    按任意键继续。 . .

    因此,总而言之,您必须在第一个此类列表期间将每个 ini 选项包含在 add_options() 列表中,否则您将遇到与程序相同的问题,引发未知选项错误并且无法从文件中获取数据或者使用 parse_config_file 的 bool 参数(使用 true 忽略未知选项)。

    【讨论】:

      猜你喜欢
      • 2015-01-12
      • 2014-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多