c++ - 当通过 boost::program_options 使用多个源时,使用最后一个而不是第一个存储值

标签 c++ boost configuration command-line-arguments boost-program-options

我的目标是将大部分时间不会更改的配置值保存在一个或多个 INI 文件中,并从命令行读取其余参数。我正在使用 boost::program_options 来实现这一点。

基本上,

options_description options;
variables_map vm;
ifstream ifs("config.ini");
store(parse_config_file(ifs, options), vm);
// !!! Does not overwrite values from config.ini
store(command_line_parser(argc, argv).options(options).run(), vm);

只要 INI 和命令行中提供的参数不重叠,这就可以很好地工作。万一它们重叠,期望的和合乎逻辑的行为是命令行中的参数覆盖以前从 INI 文件中读取的任何内容。但是,这不起作用,因为 Boost 的默认行为是使用第一个遇到的值并忽略之后的任何其他值。

您有什么想法可以绕过这个限制吗?

我正在做的一个完整的工作示例:

#include <iostream>
#include <fstream>
#include <vector>
#include <boost/program_options.hpp>

using namespace std;
using namespace boost::program_options;

// Parsed values will be stored in these
std::vector<std::string> inputFiles;
int numThreads;
bool enableX;

void parseOptions(const int argc, const char* argv[])
{
    //
    // Define our config parameters
    //
    options_description basicOptions("Main options");
    basicOptions.add_options()
        ("help,h", "Show full help.")
        ("version,V", "Show version.")
        ("config,c",
         value<vector<string>>()->multitoken()->composing(),
         "Path of a configuration INI file. Can be used repeatedly.\n"
         "The available config parameters are listed in the full help info."
        )
        ("inputFile,i",
         value<vector<string>>()->multitoken()->composing()->required(),
         "Input file(s). Can be used repeatedly."
        );
    options_description configOptions("Configuration");
    configOptions.add(basicOptions);
    configOptions.add_options()
        ("numThreads",
         value<>(&numThreads)->default_value(1),
         "Number of processing threads."
        )
        // ... snip ...
        ("enableX",
         value<>(&enableX)->default_value(true),
         "Whether to enable X."
        );

    //
    // Parse
    //
    variables_map vm;

    // Parse the minimal set of command line arguments first
    parsed_options parsedCL = command_line_parser(argc, argv).
        options(basicOptions).allow_unregistered().run();
    store(parsedCL, vm);

    // Load configuration from INI files
    if (vm.count("config"))
    {
        for (int i = 0; i < vm["config"].as<vector<string>>().size(); i++)
        {
            ifstream ifs(vm["config"].as<vector<string>>()[i].c_str());
            store(parse_config_file(ifs, configOptions), vm);
        }
    }

    // Parse and store the remaining command line arguments
    // !!! store() does not overwrite existing values !!!
    store(command_line_parser(
              collect_unrecognized(parsedCL.options, include_positional)
          ).options(configOptions).run(), vm);

    // Finally, check that that the provided input is valid
    notify(vm);
}

int main(const int argc, const char* argv[])
{
    try
    {
        parseOptions(argc, argv);
        cout << "enableX: " << enableX << endl;
    }
    catch (exception e)
    {
        cerr << e.what() << endl;
    }
    return 0;
}

如果用--config config.ini --enableX false运行上面的程序,config.ini的内容为

numThreads = 4
enableX = true

它输出“enableX: 1”,而不是“enableX: 0”——命令行中的所需值。

在一个 hackish 修复尝试中,我尝试在存储任何新参数之前简单地从 variable_map 中删除冲突参数:

void store_with_overwrite(const parsed_options& parsed, variables_map& vm)
{
    for (const option& option : parsed.options)
    {
        auto it = vm.find(option.string_key);
        if (it != vm.end())
        {
            vm.erase(it);
        }
    }
    store(parsed, vm);
}

这是行不通的,因为 Boost 也将第一个遇到的值存储在私有(private) m_final 成员变量中,即使参数已从映射中删除,它也会被使用。

我现在能想到的唯一解决方案是首先解析命令行参数,然后在 store() 之前从 INI 文件中删除 parsed_options 中的任何冲突值他们。缺点是我必须手动跟踪使用 multitoken()composing() 的选项,它们不能被删除。

必须有更好的方法。你有什么建议吗?

最佳答案

由于第一个存储的值是最后的值,这也是我希望命令行的值,解决方案实际上很简单:首先从命令行解析和存储所有内容。

通常,以与您希望它们相互“覆盖”的顺序相反的顺序存储来自不同来源的值。

variables_map vm;

// Parse the command line arguments first
parsed_options parsedCL = command_line_parser(argc, argv).
    options(configOptions).run();
store(parsedCL, vm);

// Load configuration from INI files
if (vm.count("config"))
{
    for (int i = 0; i < vm["config"].as<vector<string>>().size(); i++)
    {
        ifstream ifs(vm["config"].as<vector<string>>()[i].c_str());
        store(parse_config_file(ifs, configOptions), vm);
    }
}

// Finally, check that that the provided input is valid
notify(vm);

关于c++ - 当通过 boost::program_options 使用多个源时,使用最后一个而不是第一个存储值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37661273/

相关文章:

C++/g++ : How does the compiler handle memory-allocation in this situation?

c++ - boost::program_options 可以使用 "-"以外的定界符吗?

c++ - 遍历字符串中的行?

c++ - 包含特定 boost header 时标准非 boost 文件中的错误

Hadoop IOException 登录失败

c++ - 当对象超出范围时是否调用析构函数?

c++ - 对所有成员函数和属性使用 "this"是不好的做法吗?

c++ - 使用额外参数 boost 变体访问者

windows-8 - 在 Windows 8/商店应用程序中禁用触摸反馈(通过代码)?

javascript - 在浏览器中使用 nconf?