#c #boost #boost-program-options
#c #boost #boost-program-options
Вопрос:
В настоящее время я пытаюсь переработать некоторый код, который был передан мне. Исходный пункт кода — прочитать файл конфигурации и настроить различные параметры в файле в boost::program_options::variable_map, который затем считывается в других частях кода, который уже работает нормально.
Вот код, который я пытаюсь заменить:
// Some helpful definitions
boost::program_options::variables_map vm;
std::string filecfg = "File_name";
std::ifstream ifs(filecfg.c_str());
// Setting options (This is command line example, but config file is the same)
boost::program_options::options_description df("Command Line");
df.add_options()
("help", "display help message")
("file-cfg,f", boost::program_options::value<std::string>(), "config file")
("version", "display version and revision number");
boost::program_options::parsed_options parsedc = boost::program_options::parse_config_file(ifs, df, true);
boost::program_options::store(parsedc, vm);
boost::program_options::notify(vm);
std::vector <std::string> unrc = boost::program_options::collect_unrecognized(parsedc.options, boost::program_options::include_positional)
Я думаю, что это просто заменить boost::program_options::parsed_options parsedc и создать этот объект самостоятельно. Проблема, с которой я сталкиваюсь, заключается просто в том, что нет документации о том, как это сделать. Я думаю, это в основном потому, что он не предназначен для использования таким образом.
В любом случае, я просто хочу заполнить объект виртуальной машины параметрами, описанными в dc, и значениями, которые я могу хранить в отдельной структуре данных (например, в векторе).
Возможно ли просто добавить значения в vm? Или я должен пройти через такую функцию, как boost::program_options::store()?
Любая помощь была бы с благодарностью! Дайте мне знать, если что-то неясно, или если есть что-то, что вы хотели бы, чтобы я попробовал!
Спасибо!
Ответ №1:
Да, вы можете.
Имейте в виду, что вам придется решить, как «издеваться» / «подделывать» другую его семантику. (Например, вы можете захотеть замаскировать параметры как значения по умолчанию)
Концептуально variable_map был бы map<string, variable_value>
. variable_value
:
Класс, содержащий значение параметра. Содержит подробную информацию о том, как устанавливается значение, и позволяет удобно получать значение.
Обратите также внимание, что из-за variable_value
использования boost::any
для хранения вам нужно будет быть точным в отношении типов, которые вы будете хранить. (Так что не сохраняйте, "oops"
если вам нужно std::string("ah okay")
).
Вот простая демонстрация:
#include <boost/program_options.hpp>
#include <iostream>
#include <iomanip>
namespace po = boost::program_options;
using namespace std::string_literals;
int main(/*int argc, char** argv*/) {
// Some helpful definitions
po::variables_map vm;
vm.emplace("file-cfg", po::variable_value("string"s, true));
vm.emplace("log-level", po::variable_value(3, false));
vm.emplace("option3", po::variable_value{});
notify(vm);
std::vector unrc = { "unrecognized"s, "options"s };
for (autoamp; [name, value] : vm) {
std::cout
<< "Name: " << name
<< std::boolalpha
<< "tdefaulted:" << value.defaulted()
<< "tempty:" << value.empty();
if (typeid(std::string) == value.value().type())
std::cout << " - string " << std::quoted(value.as<std::string>()) << "n";
else if (typeid(int) == value.value().type())
std::cout << " - int " << value.as<int>() << "n";
else if (!value.empty())
std::cout << " - unknown typen";
}
}
С принтами
Name: file-cfg defaulted:true empty:false - string "string"
Name: log-level defaulted:false empty:false - int 3
Name: option3 defaulted:false empty:true
Ответ №2:
Я предупреждаю вас не использовать vm.emplace(…, po::variable_value(…,…))
.
Это довольно обманчиво: в какой-то степени это сработает, но в другом месте произойдет впечатляющий сбой.
Когда вы используете po::store
, он внутренне также создает po::variable_value
и копирует семантику вашего параметра в частное поле po::variable_value
. Нет способа установить эту семантику самостоятельно.
(см.: https://github.com/boostorg/program_options/blob/develop/src/variables_map.cpp#L83).
Без семантики вы не можете, по крайней мере:
- прочитайте параметр из нескольких источников
vm.notify()
не будет записывать значение в переменные, связанные с параметром
Вот (возможно, уродливый) способ, который должен избежать этих проблем:
po::variables_map vm;
po::options_description opts;
opts.add_options()("optName", …);
…
po::parsed_options parsedOpts(amp;opts);
pOpts.options.push_back(po::option("optName", {"optValue"}));
po::store(parsedOpts, vm);
Сначала нужно создать parsedOpts
объект, который хранит описание опций. Затем добавьте имя параметра и список значений в виде строк, независимо от типа параметра. Наконец, внутри po::store
имя и значения анализируются и сохраняются в vm
.
Полный рабочий пример:
#include <boost/program_options.hpp>
#include <iostream>
namespace po = boost::program_options;
int main(int ac, char **av) {
int i = 0;
po::variables_map vm;
po::options_description opts;
opts.add_options()("id", po::value<int>(amp;i)->default_value(1));
int userInput;
std::cin >> userInput;
if (userInput == 2) {
vm.emplace("id", po::variable_value(userInput, false));
}
if (userInput == 3) {
po::parsed_options pOpts(amp;opts);
pOpts.options.push_back(po::option("id", {std::to_string(userInput)}));
po::store(pOpts, vm);
}
po::store(po::parse_command_line(ac, av, opts), vm);
//po::store(po::parse_config_file("options.ini", opts, true), vm);
po::notify(vm);
std::cout << "id (notified): " << i << std::endl;
std::cout << "id (from vm): " << vm["id"].as<int>() << std::endl;
return 0;
}
Ввод 2
выполняется vm.emplace
и выдает:
id (notified): 0
id (from vm): 2
что плохо, так как po::value<int>(amp;i)
было проигнорировано
Ввод 2
и добавление --id=4
аргумента командной строки приводит:
terminate called after throwing an instance of 'boost::wrapexcept<boost::program_options::multiple_occurrences>'
что плохо, поскольку вы не можете использовать несколько источников
Ввод 3
запускает po::store
вручную po::parsed_options
и выдает:
id (notified): 3
id (from vm): 3
Ввод 3
и добавление --id=4
аргумента командной строки приводит:
id (notified): 3
id (from vm): 3
Что является правильным и ожидаемым, для последующих хранилищ не должно заменять значения, которые были сохранены ранее
(см. https://www.boost.org/doc/libs/1_80_0/doc/html/program_options/tutorial.html#id-1.3.30.4.5)