Подтвердить что ты не робот

Boost parse_config_file, пустое значение ключа

Я использую Boost program_options для синтаксического анализа файла конфигурации стандартным образом показанном в файле примеров multiple_sources.cpp файла program_options.

    ifstream ini_file("config.ini");  
    po::store(po::parse_config_file(ini_file, desc, true), vm);  
    po::notify(vm);

Однако в файле config.ini есть пустые пары key = value, такие как:

key1=value1  
key2=value2  
key3=  
key4=  
key5=value5  

Попытка прочитать этот файл приводит к ошибке Boost:

boost::program_options::invalid_option_value

Есть ли какой-либо механизм в boost:: program_options для чтения таких файлов конфигурации с пустыми?

Любая помощь будет высоко оценена.


Я редактирую этот вопрос, поскольку проблема еще не решена. Я просто приведу пример из Boost (1.53).

Это полный файл multiple_sources.cpp:

#include <boost/program_options.hpp>
namespace po = boost::program_options;


#include <iostream>
#include <fstream>
#include <iterator>
using namespace std;

// A helper function to simplify the main part.
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
    copy(v.begin(), v.end(), ostream_iterator<T>(os, " ")); 
    return os;
}


int main(int ac, char* av[])
{
    try {
        int opt;
        string config_file;

        // Declare a group of options that will be 
        // allowed only on command line
        po::options_description generic("Generic options");
        generic.add_options()
            ("version,v", "print version string")
            ("help", "produce help message")
            //("optimization","optimization level")      
            ("config,c", po::value<string>(&config_file)->default_value("multiple_sources.cfg"),
                  "name of a file of a configuration.")
            ;

        // Declare a group of options that will be 
        // allowed both on command line and in
        // config file
        po::options_description config("Configuration");
        config.add_options()
            ("optimization", po::value<int>(&opt)->default_value(10), 
                  "optimization level")
            ("include-path,I", po::value< vector<string> >()->composing(), 
                 "include path")
            ;

        // Hidden options, will be allowed both on command line and
        // in config file, but will not be shown to the user.
        po::options_description hidden("Hidden options");
        hidden.add_options()
            ("input-file", po::value< vector<string> >(), "input file")
            ;

        po::options_description cmdline_options;
        cmdline_options.add(generic).add(config).add(hidden);

        po::options_description config_file_options;
        config_file_options.add(config).add(hidden);

        po::options_description visible("Allowed options");
        visible.add(generic).add(config);

        po::positional_options_description p;
        p.add("input-file", -1);

        po::variables_map vm;
        store(po::command_line_parser(ac, av).
              options(cmdline_options).positional(p).run(), vm);
        notify(vm);

        ifstream ifs(config_file.c_str());
        if (!ifs)
        {
            cout << "can not open config file: " << config_file << "\n";
            return 0;
        }
        else
        {
            store(parse_config_file(ifs, config_file_options), vm);
            notify(vm);
        }

        if (vm.count("help")) {
            cout << visible << "\n";
            return 0;
        }

        if (vm.count("version")) {
            cout << "Multiple sources example, version 1.0\n";
            return 0;
        }

        if (vm.count("include-path"))
        {
            cout << "Include paths are: " 
                 << vm["include-path"].as< vector<string> >() << "\n";
        }

        if (vm.count("input-file"))
        {
            cout << "Input files are: " 
                 << vm["input-file"].as< vector<string> >() << "\n";
        }

        cout << "Optimization level is " << opt << "\n";                
    }
    catch(exception& e)
    {
        cout << e.what() << "\n";
        return 1;
    }    
    return 0;
}

И соответствующий файл конфигурации (multiple_sources.cfg):

#
# Comment out this line to use hard-coded default value of 10
# 
optimization = 1
include-path = /opt

Если этот файл теперь изменен на:

#
# Comment out this line to use hard-coded default value of 10
# 
optimization = 
include-path = /opt

Выдается следующее сообщение об ошибке:

the argument for option 'optimization' is invalid

Предлагаемые решения с проверкой перегрузки и т.д. кажутся излишне сложными, тем более, что для каждого типа данных может потребоваться написать функцию проверки, включающую возможность распознавания "\n" другого белого пространства.

Любые предложения? Спасибо всем за то, что нашли время.


Следуя предложению Aditya, я заменил следующую строку:

            ("optimization", po::value<int>(&opt)->default_value(10), 
                  "optimization level")

со следующим:

            ("optimization", po::value<int>(&opt)->zero_tokens(), 
                  "optimization level")

и:

            ("optimization", po::value<int>(&opt)->implicit_value(10), 
                  "optimization level")

и ни один из них не читает пустые опции. Пример примера программы "parser_test.cpp" обходит использование zero_tokens() или implicit_value() путем считывания пар ключ-значение в вектор следующим образом:

void test_config_file(const char* config_file)
{
    options_description desc;
    desc.add_options()
        ("gv1", new untyped_value)
        ("gv2", new untyped_value)
        ("empty_value", new untyped_value)
        ("plug*", new untyped_value)
        ("m1.v1", new untyped_value)
        ("m1.v2", new untyped_value)
        ("b", bool_switch())
    ;

    const char content1[] =
    " gv1 = 0#asd\n"
    "empty_value = \n"
    "plug3 = 7\n"
    "b = true\n"
    "[m1]\n"
    "v1 = 1\n"
    "\n"
    "v2 = 2\n"    
    ;

    vector<option> a2 = parse_config_file<char>(config_file, desc).options;
    BOOST_REQUIRE(a2.size() == 6);
    check_value(a2[0], "gv1", "0");
    check_value(a2[1], "empty_value", "");
    check_value(a2[2], "plug3", "7");
    check_value(a2[3], "b", "true");
    check_value(a2[4], "m1.v1", "1");
    check_value(a2[5], "m1.v2", "2");
}
4b9b3361

Ответ 1

Я бы рекомендовал вам окружить с помощью try/catch это исключение и выбросить ошибки только для тех полей, которые действительно необходимы для вашей программы и не могут быть пустыми, иначе поле будет проигнорировано.

Ответ 2

Нет, в настоящее время нет способа справиться с этим в boost:: program_options. Наличие пустого ключа: пара значений в вашем файле INI совпадает с заданием опции в командной строке без предоставления аргумента. Подход к написанию пользовательских валидаторов, предложенный ypnos, может работать, но кажется непрактичным, так как вам нужно будет применить его к каждой ожидаемой вами опции, которая может быть оставлена ​​пустой. Вам нужно будет написать собственную реализацию po:: parse_config_file, которая будет игнорировать строки без значения (если соответствующая опция не помечена как zero_token), чтобы получить результат, который вы ищете.