输入流运算符查找顺序与Boost.Program_options



我在命名空间fw::example中有一个枚举类和一个相应的输入流运算符。

#include <stdexcept>
#include <string>
#include <istream>
namespace fw {
namespace example {
enum class Booleans {
  kFalse = 0,
  kTrue = 1
};
std::istream& operator>>(std::istream& is, Booleans& boolean) {
  std::string token;
  is >> token;
  if ("true" == token) {
    boolean = Booleans::kTrue;
  } else if ("false" == token) {
    boolean = Booleans::kFalse;
  } else {
    throw std::invalid_argument{
        "Invalid string representation for an enumerator of the Booleans class."};
  }
  return is;
}
}  // namespace example
}  // namespace fw

然后我通过Boost.Program_options在同一命名空间中绑定该枚举的变量:

// [...]
description_.add_options()(
"bool",
boost::program_options::value<Booleans>(&boolean_)
    ->required()
    ->default_value(Booleans::kFalse),
"A boolean string, either true or false.");
// [...]

但我不想向用户公开std::runtime_error,而是想要使用框架中的适当异常,那就是boost::program_options::invalid_option_value .想象一个更复杂的场景,其中枚举类和输入流运算符在库中定义,我无法修改该库。

所以我尝试了以下内容(根据我在SO上找到的答案,我再也找不到了(:

namespace boost {
namespace program_options {
std::istream& operator>>(std::istream& is,
                         fw::example::Booleans& boolean) {
  try {
    return fw::example::operator>>(is, boolean);
  } catch (std::invalid_argument const& kException) {
    throw invalid_option_value{kException.what()};
  }
}
}  // namespace program_options
}  // namespace boost

整个代码编译,但问题是,只调用了自由函数fw::example::operator>>,因此传播了错误的执行:

terminate called after throwing an instance of 'std::invalid_argument'
  what():  Invalid string representation for an enumerator of the Booleans class.

我正在寻找一种解决方案,能够将Boost.Program_options相关的代码与其他代码完全分开,但也使用适当的异常。

我正在使用以下环境:

  • 提升 v1.49
  • 带有-std=c++11参数的 GCC v4.71。

问题似乎是您正在使用流式处理运算符进行验证。

相反,请提供自定义验证程序来执行验证。

在这种情况下,您不再与操作员的行为发生冲突<<。


program_options 命名空间中添加竞争重载时,您所能希望的最好的结果是编译器会发现重载可以接受/too/因此无法使用"模棱两可的重载"进行编译

感谢用户@sehe,我想出了本答案中提出的解决方案,使用标准流运算符和Boost.Program_options的自定义验证器。

忽略 DRY 问题(如提到的源注释所示(,解决方案看起来很干净。我还纳入了 https://stackoverflow.com/a/5517755/891439 中描述的常见陷阱。

使用 GCC v4.81 和 Boost.Program_options v1.56 的源代码:

#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <sstream>
#include <string>
#include "boost/any.hpp"
#include "boost/program_options.hpp"
// Imagine the source code in the following namespace block is placed in an
// external library.
// The mapping between enumerators and strings violates the DRY principle, but
// that issue can be easily fixed, e. g. by using one [Boost.Bimap][4].
namespace fw {
namespace example {
enum class Booleans : std::uint8_t {
  kFalse = 0,
  kTrue = 1
};
std::ostream& operator<<(std::ostream& os, Booleans const& kBoolean) {
  switch (kBoolean) {
    case Booleans::kFalse:
      os << "false";
      break;
    case Booleans::kTrue:
      os << "true";
      break;
    default:
      os.setstate(std::ios_base::failbit);
      break;
  }
  return os;
}
std::istream& operator>>(std::istream& is, Booleans& boolean) {
  std::string token;
  is >> token;
  if ("true" == token) {
    boolean = Booleans::kTrue;
  } else if ("false" == token) {
    boolean = Booleans::kFalse;
  } else {
    is.setstate(std::ios_base::failbit);
  }
  return is;
}
}  // namespace example
}  // namespace fw
// The following source code is application specific code.
namespace fw {
namespace example {
void validate(boost::any& value,
              std::vector<std::string> const& kValues,
              fw::example::Booleans*,
              int) {
  boost::program_options::validators::check_first_occurrence(value);
  std::string const& kStringValue{
      boost::program_options::validators::get_single_string(kValues)};
  std::istringstream iss{kStringValue};
  Booleans real_value;
  iss >> real_value;
  if (!iss) {
    throw boost::program_options::validation_error{
        boost::program_options::validation_error::invalid_option_value};
  }
  value = boost::any{real_value};
}
}  // namespace example
}  // namespace fw
int main(int argc, char** argv) {
  fw::example::Booleans boolean;
  boost::program_options::variables_map variables_map;
  boost::program_options::options_description options_description{
      "Allowed Options"};
  options_description.add_options()
      ("help,h", "Display the help message.")
      ("bool",
       boost::program_options::value<fw::example::Booleans>(
           &boolean)->required(),
       "Set the boolean value.");
  try {
    boost::program_options::store(
        boost::program_options::parse_command_line(argc,
                                                   argv,
                                                   options_description),
        variables_map);
    if (variables_map.count("help")) {
      std::cout << options_description << "n";
      return 1;
    }
    boost::program_options::notify(variables_map);
  } catch (boost::program_options::error const &kException) {
    std::cerr << "Program Options Error: "
              << kException.what()
              << "nnSpecify -help for further information.n";
    return 2;
  }
  if (variables_map.count("bool")) {
      std::cout << "The boolean value was set to "
                << variables_map["bool"].as<fw::example::Booleans>()
                << ".n";
  }
}
  1. 任何人都可以提供进一步的改进建议吗?
  2. 关于源代码的任何一般性评论?

最新更新