如何解决使用 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;
}
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 举报,一经查实,本站将立刻删除。