具有隐含值的Boost程序选项错误地获取位置选项的值

问题描述 投票:0回答:2

我的应用程序的 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

如何强制将具有隐式值的选项与位置选项分开?或者有其他解决方案吗?

c++ boost-program-options
2个回答
2
投票

显然,命令行解析器无法区分这两种情况:

  1. 隐式选项,后跟一个值,该值应该替换预定义的隐式值
  2. 隐式选项,后跟(甚至是必需的)位置参数

因此,它盲目地决定采用案例#1。您仍然可以通过双破折号标记选项结尾 - 然后解析器将获取输入文件名:

./application.exe -d Frobnigator -l -- /path/to/input/file.xml

我认为这个逻辑是有道理的,因为它很简单,否则用户会很难记住哪些位置参数是必需的,哪些不是。


0
投票

一段时间后回到这个问题,我相信这是 boost

program_options
模块的一个错误。特别是,即使将解析器样式设置为排除
short_allow_next
,从而要求所有短选项都直接使用其参数,
program_options
仍然会继续并将非选项参数分配给前面的选项,如果它的
max_tokens()
计数尚未达到,而不是位置选项。

当查看

cmdline.cpp
中解析器的源代码时,我们会发现有两个地方收集选项的值:

  1. 在方法
    finish_option()
    中,它将分配任何存在的立即值,但对于从命令行分配更多值而言,其行为是非贪婪的。
  2. 在主方法
    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");
© www.soinside.com 2019 - 2024. All rights reserved.