boost::program_options:带有固定和可变标记的参数?

2024-05-17

是否可以在 boost::program_options 中使用此类参数?

program  -p1 123 -p2 234 -p3 345 -p12 678

即,是否可以使用第一个标记指定参数名称(例如-p) 后跟一个数字,是动态的吗?
我想避免这种情况:

program  -p 1 123 -p 2 234 -p 3 345 -p 12 678

Boost.ProgramOptions 不为此提供直接支持。然而,有两种通用的解决方案,各有其优缺点:

  • 通配符选项。
  • 自定义解析器。

通配符选项

如果可以接受使用--p代替-p,则可以使用通配符选项。这需要迭代variables_map在提取期间,因为 Boost.ProgramOptions 不支持在重载中接收键和值validate()功能。

#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <vector>

#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>

typedef std::map<int, int> p_options_type;

/// @brief Extract options from variable map with the a key of
///        <prefix>#*.
p_options_type get_p_options(
  const boost::program_options::variables_map& vm,
  const std::string prefix)
{
  p_options_type p_options;

  const std::size_t prefix_size = prefix.size();

  boost::iterator_range<std::string::const_iterator> range;
  namespace po = boost::program_options;
  BOOST_FOREACH(const po::variables_map::value_type& pair, vm)
  {
    const std::string& key = pair.first;

    // Key is too small to contain prefix and a value, continue to next.
    if (key.size() < (1 + prefix.size())) continue;

    // Create range that partitions key into two parts.  Given a key
    // of "p12" the resulting partitions would be:
    //
    //     ,--------- key.begin           -., prefix = "p"
    //    / ,-------- result.begin        -:, post-prefix = "12"
    //   / /   ,----- key.end, result.end -'
    //  |p|1|2|
    range = boost::make_iterator_range(key.begin() + prefix_size,
                                       key.end());

    // Iterate to next key if the key:
    // - does not start with prefix
    // - contains a non-digit after prefix
    if (!boost::starts_with(key, prefix) || 
        !boost::all(range, boost::is_digit()))
      continue;

    // Create pair and insert into map.
    p_options.insert(
      std::make_pair(
        boost::lexical_cast<int>(boost::copy_range<std::string>(range)),
        pair.second.as<int>())); 
  }
  return p_options;
}

int main(int ac, char* av[])
{
  namespace po = boost::program_options;
  po::options_description desc;
  desc.add_options()
    ("p*", po::value<int>())
    ;

  po::variables_map vm;
  store(po::command_line_parser(ac, av).options(desc).run(), vm);

  BOOST_FOREACH(const p_options_type::value_type& p, get_p_options(vm, "p"))
  {
    std::cout << "p" << p.first << "=" << p.second << std::endl;
  }
}

及其用法:


./a.out --p1 123 --p2 234 --p3=345 --p12=678
p1=123
p2=234
p3=345
p12=678  

这种方法需要迭代整个映射来识别通配符匹配,从而导致复杂性O(n)。此外,它需要修改所需的语法,其中--p1 123需要使用而不是-p1 123。此限制是 Boost.ProgramOptions 的默认解析器行为的结果,其中单个连字符应该后跟单个字符。


自定义解析器

另一种方法是添加一个自定义解析器 http://www.boost.org/doc/libs/1_48_0/doc/html/boost/program_options/basic_command_line_parser.html#id1101702-bb to the command_line_parser。自定义解析器将允许-p1语法,以及其他常见形式,例如--p1 123 and -p1=123。有以下几种行为需要处理:

  • 解析器一次将接收一个令牌。这样,它将收到p1 and 123关于单独的调用。解析器有责任配对p1 to 123.
  • Boost.ProgramOptions 期望至少有一个解析器来处理令牌。否则boost::program_options::unknown_option http://www.boost.org/doc/libs/1_53_0/doc/html/boost/program_options/unknown_option.html会被抛出。

为了考虑这些行为,自定义解析器将管理状态并执行编码/解码:

  • 当解析器接收到p1,它提取1,将状态存储在解析器中。此外,它还编码了一个无操作价值p.
  • 当解析器接收到123,它将它与存储的状态一起编码为值p.

因此,如果解析器接收到-p1 and 123, 2 个值被插入到variables_map for p: the 无操作值和1:123.



{ "p" : [ "no operation",
          "1:123" ] }  

通过提供帮助函数来转换编码的内容,这种编码对用户来说是透明的p矢量到地图中。解码的结果将是:


{ 1 : 123 }  

这是示例代码:

#include <iostream>
#include <map>
#include <string>
#include <utility> // std::pair, std::make_pair
#include <vector>

#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>

typedef std::map<int, int> p_options_type;

/// @brief Parser that provides the ability to parse "-p# #" options.
///
/// @note The keys and values are passed in separately to the parser.
///       Thus, the struct must be stateful.
class p_parser
{
public:

  explicit
  p_parser(const std::string& prefix)
    : prefix_(prefix),
      hyphen_prefix_("-" + prefix)
  {}

  std::pair<std::string, std::string> operator()(const std::string& token)
  {
    // To support "-p#=#" syntax, split the token.
    std::vector<std::string> tokens(2);
    boost::split(tokens, token, boost::is_any_of("="));

    // If the split resulted in two tokens, then key and value were
    // provided as a single token.
    if (tokens.size() == 2)
      parse(tokens.front()); // Parse key.

    // Parse remaining token.
    // - If tokens.size() == 2, then the token is the value.
    // - Otherwise, it is a key.
    return parse(tokens.back());
  }

  /// @brief Decode a single encoded value.
  static p_options_type::value_type decode(const std::string& encoded)
  {
    // Decode.
    std::vector<std::string> decoded(field_count_);
    boost::split(decoded, encoded, boost::is_any_of(delimiter_));

    // If size is not equal to the field count, then encoding failed.
    if (field_count_ != decoded.size())
      throw boost::program_options::invalid_option_value(encoded);

    // Transform.
    return std::make_pair(boost::lexical_cast<int>(decoded[0]),
                          boost::lexical_cast<int>(decoded[1]));
  }

  /// @brief Decode multiple encoded values.
  static p_options_type decode(const std::vector<std::string>& encoded_values)
  {
    p_options_type p_options;
    BOOST_FOREACH(const std::string& encoded, encoded_values)
    {
      // If value is a no-op, then continue to next.
      if (boost::equals(encoded, noop_)) continue;
      p_options.insert(decode(encoded));
    }
    return p_options;
  }

private:

  std::pair<std::string, std::string> parse(const std::string& token)
  {
    return key_.empty() ? parse_key(token)
                        : parse_value(token);
  }

  /// @brief Parse key portion of option: "p#"
  std::pair<std::string, std::string> parse_key(const std::string& key)
  {
    // Search for the prefix to obtain a range that partitions the key into
    // three parts.  Given --p12, the partitions are:
    //
    //      ,--------- key.begin    -., pre-prefix   = "-"
    //     / ,-------- result.begin -:, prefix       = "-p"
    //    / /   ,----- result.end   -:, post-prefix  = "12"
    //   / /   /   ,-- key.end      -'
    //  |-|-|p|1|2|
    //
    boost::iterator_range<std::string::const_iterator> result =
      boost::find_first(key, prefix_);

    // Do not handle the key if:
    // - Key end is the same as the result end.  This occurs when either
    //   either key not found or nothing exists beyond the key (--a or --p)
    // - The distance from start to prefix start is greater than 2 (---p)
    // - Non-hyphens exists before prefix (a--p)
    // - Non-numeric values are after result.
    if (result.end() == key.end() ||
        distance(key.begin(), result.begin()) > 2 ||
        !boost::all(
          boost::make_iterator_range(key.begin(), result.begin()),
          boost::is_any_of("-")) ||
        !boost::all(
          boost::make_iterator_range(result.end(), key.end()),
          boost::is_digit()))
    {
      // A different parser will handle this token.
      return make_pair(std::string(), std::string());
    }

    // Otherwise, key contains expected format.
    key_.assign(result.end(), key.end());

    // Return non-empty pair, otherwise Boost.ProgramOptions will
    // consume treat the next value as the complete value.  The
    // noop entries will be stripped in the decoding process.
    return make_pair(prefix_, noop_);
  }

  /// @brief Parse value portion of option: "#"
  std::pair<std::string, std::string> parse_value(const std::string& value)
  {
    std::pair<std::string, std::string> encoded =
      make_pair(prefix_, key_ + delimiter_ + value);
    key_.clear();
    return encoded;
  }

private:
  static const int field_count_ = 2;
  static const std::string delimiter_;
  static const std::string noop_;
private:
  const std::string prefix_;
  const std::string hyphen_prefix_;
  std::string key_;
};

const std::string p_parser::delimiter_ = ":";
const std::string p_parser::noop_      = "noop";

/// @brief Extract and decode options from variable map.
p_options_type get_p_options(
  const boost::program_options::variables_map& vm,
  const std::string prefix)
{
  return p_parser::decode(vm[prefix].as<std::vector<std::string> >());
}

int main(int ac, char* av[])
{
  const char* p_prefix = "p";
  namespace po = boost::program_options;

  // Define options.
  po::options_description desc;
  desc.add_options()
    (p_prefix, po::value<std::vector<std::string> >()->multitoken())
    ;

  po::variables_map vm;
  store(po::command_line_parser(ac, av).options(desc)
          .extra_parser(p_parser(p_prefix)).run()
       , vm);

  // Extract -p options. 
  if (vm.count(p_prefix))
  {
    // Print -p options.
    BOOST_FOREACH(const p_options_type::value_type& p,
                  get_p_options(vm, p_prefix))
    {
      std::cout << "p" << p.first << "=" << p.second << std::endl;
    }
  }
}

及其用法:


./a.out -p1 123 --p2 234 -p3=345 --p12=678
p1=123
p2=234
p3=345
p12=678  

除了是一个更大的解决方案之外,一个缺点是需要经过解码过程才能获得所需的值。不能简单地迭代结果vm["p"]以一种有意义的方式。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

boost::program_options:带有固定和可变标记的参数? 的相关文章

  • 部署 MVC4 项目时出错:找不到文件或程序集

    过去 我只需使用 Visual Studio 2012 发布到 AWS 菜单项即可部署我的 MVC4 网站 到 AWS Elastic Beanstalk 现在 程序可以在本地编译并运行 但无法部署 从消息来看 它似乎正在寻找不在当前部署的
  • 属性对象什么时候创建?

    由于属性实际上只是附加到程序集的元数据 这是否意味着属性对象仅根据请求创建 例如当您调用 GetCustomAttributes 时 或者它们是在创建对象时创建的 或者 前两个的组合 在由于 CLR 的属性扫描而创建对象时创建 从 CLR
  • Signalr 在生产服务器中总是陷入长轮询

    当我在服务器中托管应用程序时 它会检查服务器端事件并始终回退到长轮询 服务器托管环境为Windows Server 2012 R1和IIS 7 5 无论如何 我们是否可以解决这个问题 https cloud githubuserconten
  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • FFMPEG Seeking 带来音频伪影

    我正在使用 ffmpeg 实现音频解码器 在读取音频甚至搜索已经可以工作时 我无法找到一种在搜索后清除缓冲区的方法 因此当应用程序在搜索后立即开始读取音频时 我没有任何工件 avcodec flush buffers似乎对内部缓冲区没有任何
  • fgets() 和 Ctrl+D,三次才能结束?

    I don t understand why I need press Ctrl D for three times to send the EOF In addition if I press Enter then it only too
  • C# 中可空类型是什么?

    当我们必须使用nullable输入 C net 任何人都可以举例说明 可空类型 何时使用可空类型 https web archive org web http broadcast oreilly com 2010 11 understand
  • 将字符串从非托管代码传递到托管

    我在将字符串从非托管代码传递到托管代码时遇到问题 在我的非托管类中 非托管类 cpp 我有一个来自托管代码的函数指针 TESTCALLBACK FUNCTION testCbFunc TESTCALLBACK FUNCTION 接受一个字符
  • 如何在 WPF RichTextBox 中跟踪 TextPointer?

    我正在尝试了解 WPF RichTextBox 中的 TextPointer 类 我希望能够跟踪它们 以便我可以将信息与文本中的区域相关联 我目前正在使用一个非常简单的示例来尝试弄清楚发生了什么 在 PreviewKeyDown 事件中 我
  • 使用 C# 在 WinRT 中获取可用磁盘空间

    DllImport kernel32 dll SetLastError true static extern bool GetDiskFreeSpaceEx string lpDirectoryName out ulong lpFreeBy
  • c# Asp.NET MVC 使用FileStreamResult下载excel文件

    我需要构建一个方法 它将接收模型 从中构建excel 构建和接收部分完成没有问题 然后使用内存流导出 让用户下载它 不将其保存在服务器上 我是 ASP NET 和 MVC 的新手 所以我找到了指南并将其构建为教程项目 public File
  • 当 Cortex-M3 出现硬故障时如何保留堆栈跟踪?

    使用以下设置 基于 Cortex M3 的 C gcc arm 交叉工具链 https launchpad net gcc arm embedded 使用 C 和 C FreeRtos 7 5 3 日食月神 Segger Jlink 与 J
  • 基于范围的 for 循环中的未命名循环变量?

    有没有什么方法可以不在基于范围的 for 循环中 使用 循环变量 同时也避免编译器发出有关未使用它的警告 对于上下文 我正在尝试执行以下操作 我启用了 将警告视为错误 并且我不想进行像通过在某处毫无意义地提及变量来强制 使用 变量这样的黑客
  • A* 之间的差异 pA = 新 A;和 A* pA = 新 A();

    在 C 中 以下两个动态对象创建之间的确切区别是什么 A pA new A A pA new A 我做了一些测试 但似乎在这两种情况下 都调用了默认构造函数 并且仅调用了它 我正在寻找性能方面的任何差异 Thanks If A是 POD 类
  • 使用向量的 merge_sort 在少于 9 个输入的情况下效果很好

    不知何故 我使用向量实现了合并排序 问题是 它可以在少于 9 个输入的情况下正常工作 但在有 9 个或更多输入的情况下 它会执行一些我不明白的操作 如下所示 Input 5 4 3 2 1 6 5 4 3 2 1 9 8 7 6 5 4 3
  • Windows 窗体不会在调试模式下显示

    我最近升级到 VS 2012 我有一组在 VS 2010 中编码的 UI 测试 我试图在 VS 2012 中启动它们 我有一个 Windows 窗体 在开始时显示使用 AssemblyInitialize 属性运行测试 我使用此表单允许用户
  • 更改窗口的内容 (WPF)

    我创建了一个简单的 WPF 应用程序 它有两个 Windows 用户在第一个窗口中填写一些信息 然后单击 确定 这会将他们带到第二个窗口 这工作正常 但我试图将两个窗口合并到一个窗口中 这样只是内容发生了变化 我设法找到了这个更改窗口内容时
  • 在 ASP.NET 中将事件冒泡为父级

    我已经说过 ASP NET 中的层次结构 page user control 1 user control 2 control 3 我想要做的是 当控件 3 它可以是任何类型的控件 我一般都想这样做 让用户用它做一些触发回发的事情时 它会向
  • 如何在 C# 中播放在线资源中的 .mp3 文件?

    我的问题与此非常相似question https stackoverflow com questions 7556672 mp3 play from stream on c sharp 我有音乐网址 网址如http site com aud
  • 如何连接字符串和常量字符?

    我需要将 hello world 放入c中 我怎样才能做到这一点 string a hello const char b world const char C string a hello const char b world a b co

随机推荐