微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 Boost.Program_options 解析配置文件中的 std::vector<T> 选项

如何解决使用 Boost.Program_options 解析配置文件中的 std::vector<T> 选项

众所周知,Boost.Program_options 不会像处理命令行选项那样方便地处理配置文件中的多令牌选项(以空格分隔)。我发现的 The main solution 涉及定义 custom validators。因此,我定义了以下验证器:

template<typename T>
void validate(boost::any& v,const std::vector<std::string>& values,std::vector<T>*,int) {
    std::cout << "validate called!" << std::endl;
    boost::program_options::validators::check_first_occurrence(v);
    if (values.size() == 0)
        throw boost::program_options::validation_error(boost::program_options::validation_error::kind_t::at_least_one_value_required);
    std::vector<T> numeric_values(values.size(),0);
    std::transform(values.begin(),values.end(),numeric_values.begin(),[] (const std::string&& string) {
        std::istringstream iss(string);
        T val;
        iss >> val;
        if (iss.fail())
            throw boost::program_options::validation_error(boost::program_options::validation_error::kind_t::invalid_option_value,"",string);
        return val;
    });
    v = std::move(numeric_values);
}

但是,当我尝试在配置文件中指定 std::vector 选项时,验证器永远不会被调用,并且我收到标准错误 the argument ('4 8 16 32') for option 'sizes' is invalid。相关的选项行是这样的:

("sizes",po::value<std::vector<size_t>>(&sizes)->multitoken(),"Sizes of the different tensor modes,separated by spaces")

我怀疑这个解决方在这种情况下可能会失败,因为选项是 std::vector<T> 类型,因为我知道这是由 Boost.Program_options 处理的特殊情况。我尝试寻找有关使用自定义验证器的更多文档,或查看源代码,但找不到我需要的内容

我可以想到涉及转发包装器对象的替代解决方案,这与 std::vector<T> 类型明显不同,而只是转发赋值运算符。但是,在定义选项时需要传递这些对象,而在解析过程中仍处于范围内,这意味着我必须为每个 std::vector<T> 选项定义一个临时变量,这似乎不是最佳解决方案。我希望有人知道这个问题的更好解决方案。

更新:我创建了一个可重现的小示例,表明问题仅出现在 std::vector 选项中,而不是出现在自定义 coordinate 结构中。这是源文件

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

namespace po = boost::program_options;

struct coordinate {
    double x,y;
};

struct options_storage {
    coordinate coords = {0,0};
    std::vector<int> vector = {};
};

void print_options_storage(const options_storage& options) {
    std::cout << "coords: (" << options.coords.x << "," << options.coords.y << ")" << std::endl;
    std::cout << "vector: [";
    for (size_t i = 0; i < options.vector.size(); i++) {
        std::cout << options.vector[i];
        if (i < options.vector.size() - 1)
            std::cout << ",";
    }
    std::cout << "]" << std::endl;
}

template<typename T>
void validate(boost::any& v,int) {
    std::cout << "validate-vector called!" << std::endl;
    boost::program_options::validators::check_first_occurrence(v);
        if (values.size() == 0)
                throw boost::program_options::validation_error(boost::program_options::validation_error::kind_t::at_least_one_value_required);
        std::vector<T> numeric_values(values.size(),0);
        std::transform(values.begin(),string);
        return val;
    });
        v = std::move(numeric_values);
}

// From https://stackoverflow.com/questions/5884465/boostprogram-options-config-file-option-with-multiple-tokens
void validate(boost::any& v,coordinate*,int) {
    std::cout << "validate-coordinate called!" << std::endl;
    coordinate c;
    std::vector<double> dvalues;
    for(std::vector<std::string>::const_iterator it = values.begin(); it != values.end(); ++it) {
        std::stringstream ss(*it);
        std::copy(std::istream_iterator<double>(ss),std::istream_iterator<double>(),std::back_inserter(dvalues));
        if(!ss.eof())
            throw po::validation_error(boost::program_options::validation_error::kind_t::invalid_option_value,*it);
    }
    if (dvalues.size() != 2)
        throw po::validation_error(boost::program_options::validation_error::kind_t::invalid_option_value,"");
    c.x = dvalues[0];
    c.y = dvalues[1];
    v = c;
}

int main(int argc,char** argv) {
    
    options_storage options;
    
    po::options_description desc("General");
    desc.add_options()
        ("coords",po::value<coordinate>(&options.coords)->multitoken())
        ("vector",po::value<std::vector<int>>(&options.vector)->multitoken())
    ;
    po::variables_map vm;
    
    po::store(po::parse_command_line(argc,argv,desc),vm);
    po::notify(vm);
    print_options_storage(options);
    
    std::ifstream ifs1;
    ifs1.open("config1.ini",std::ifstream::in);
    po::store(po::parse_config_file(ifs1,vm);
    po::notify(vm);
    ifs1.close();
    print_options_storage(options);
    
    std::ifstream ifs2;
    ifs2.open("config2.ini",std::ifstream::in);
    po::store(po::parse_config_file(ifs2,vm);
    po::notify(vm);
    ifs2.close();
    print_options_storage(options);
    
    return 0;
    
}

使用配置文件 config1.ini:

coords = 3.5 4.5

config2.ini:

coords = 5.5 6.5
vector = 5 6 7 8

CMakeLists.txt:

cmake_minimum_required(VERSION 3.9)
project(boost-program-options-test LANGUAGES CXX)

set(CMAKE_VERBOSE_MAKEFILE on)

find_package(Boost COMPONENTS program_options)

add_executable(po-test test.cpp)
target_link_libraries(po-test stdc++)
target_link_libraries(po-test ${Boost_LIBRARIES})
target_compile_options(po-test PRIVATE -Wall -Wextra -Wno-unkNown-pragmas -Wno-unkNown-warning-option -march=native -mtune=native -O3 -DNDEBUG)
set_target_properties(po-test PROPERTIES
    CXX_STANDARD 17
    CXX_STANDARD_required ON
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/.."
)

输出

coords: (0,0)
vector: []
validate-coordinate called!
coords: (3.5,4.5)
vector: []
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::program_options::invalid_option_value> >'
  what():  the argument ('5 6 7 8') for option 'vector' is invalid
Aborted (core dumped)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?