我的应用程序的 Boost 程序选项如下。
namespace po = boost::program_options;
desc.add_options()
( "logFile,l" , po::value<std::string>(&logFilename)->implicit_value( "trace.log" ) , "Log file name" )
( "deviceType,d" , po::value<std::string>(&deviceName)->required() , "Device type" )
( "inputFile" , po::value<std::string>(&inputFilename)->required() , "Input filename" );
po::positional_options_description positionalOptions;
positionalOptions.add( "inputFile" , -1 );
问题是,根据位置,
logFile
选项可能会错误地获取inputFile
选项的值。在示例中:
./application.exe -d Frobnigator -l /path/to/input/file.xml
其中
/path/to/input/file.xml
是输入文件,而不是日志文件,我收到错误消息 the option '--inputFile' is required but missing
。当logFile选项首先出现时不会出现这样的问题,就像这样
./application.exe -l -d Frobnigator /path/to/input/file.xml
如何强制将具有隐式值的选项与位置选项分开?或者有其他解决方案吗?
显然,命令行解析器无法区分这两种情况:
因此,它盲目地决定采用案例#1。您仍然可以通过双破折号标记选项结尾 - 然后解析器将获取输入文件名:
./application.exe -d Frobnigator -l -- /path/to/input/file.xml
我认为这个逻辑是有道理的,因为它很简单,否则用户会很难记住哪些位置参数是必需的,哪些不是。
一段时间后回到这个问题,我相信这是 boost
program_options
模块的一个错误。特别是,即使将解析器样式设置为排除 short_allow_next
,从而要求所有短选项都直接使用其参数,program_options
仍然会继续并将非选项参数分配给前面的选项,如果它的 max_tokens()
计数尚未达到,而不是位置选项。
当查看
cmdline.cpp
中解析器的源代码时,我们会发现有两个地方收集选项的值:
finish_option()
中,它将分配任何存在的立即值,但对于从命令行分配更多值而言,其行为是非贪婪的。run()
中,它贪婪地尝试将所有非选项参数分配给前面的选项,但仍然可以接受更多值。考虑到
finish_option()
工作得很好,只是 run()
后来以某种不适合隐式/可选值选项的方式重新分配事物,有一个解决方法是进一步专门化 boost::program_options::typed_value
:只需让它返回 max_tokens()
为第一次调用时为 1,后续调用时为 0。那么就无法进行馅料了:
namespace boost {
namespace program_options {
// Optional value, that only accepts values immediately:
template<class T, class charT = char>
class true_option : public typed_value<T, charT> {
using base = typed_value<T, charT>;
mutable bool max_tokens_called_before { false };
public:
unsigned max_tokens() const {
unsigned min = base::min_tokens();
unsigned max = base::max_tokens();
if(min == 0 && max == 1 && max_tokens_called_before) {
max = 0;
}
max_tokens_called_before = true;
return max;
}
using base::base;
};
}
}
使用方法如下:
unsigned level;
po::options_description desc("comparebook");
desc.add_options()
("level,l",
(new po::true_option<unsigned>(&level))
->default_value(0U)
->implicit_value(1U),
"set level");