【问题标题】:Using Custom Types with Boost Program Options使用带有 Boost 程序选项的自定义类型
【发布时间】:2013-11-17 19:50:12
【问题描述】:

我正在尝试使用 Boost ProgramOptions 解析配置文件以初始化我自己的类类型数据集(代码如下)

我将选项添加为:

config_.add_options()("dataset", po::value<Dataset>()->required(),"Dataset");

我的数据集类定义如下:

#ifndef DATASET_H_
#define DATASET_H_

#include <boost/algorithm/string/predicate.hpp>

class Dataset {
public:
  enum Name {
    JAVA, SUMATRA, ASIA, AFRICA
  };

  Dataset(Name name = JAVA) : name_(name) {}
  operator Name () const {return name_;}

  std::string asString() const;

private:
  Name name_;

  //prevent automatic conversion for any other built-in types such as bool, int, etc
  template<typename T>
  operator T() const;
};

inline std::string Dataset::asString() const {
  if (name_ == Dataset::JAVA)
    return "JAVA";
  else if (name_ == Dataset::SUMATRA)
    return "SUMATRA";
  else if (name_ == Dataset::ASIA)
    return "ASIA";
  else if (name_ == Dataset::AFRICA)
    return "AFRICA";
  else
    return "UNKNOWN DATASET";
}

inline std::ostream& operator<<(std::ostream& os, const Dataset& dataset) {
  os << dataset.asString();
  return os;
}

inline std::istream& operator>>(std::istream& in, Dataset& dataset) {
  std::string token;
  in >> token;
  if (boost::iequals(token, "java"))
    dataset = Dataset::JAVA;
  else if (boost::iequals(token, "sumatra"))
    dataset = Dataset::SUMATRA;
  else if (boost::iequals(token, "asia"))
    dataset = Dataset::ASIA;
  else if (boost::iequals(token, "africa"))
    dataset = Dataset::AFRICA;
  else
    throw std::runtime_error("Invalid Dataset Name");
  return in;
}

#endif // DATASET_H_

我以为我已经完成了所有工作,至少这是让 boost 对我的其他自定义类型感到满意所需要的。但是我收到了这个编译错误,我无法理解:

In file included from /usr/include/boost/type_traits/has_right_shift.hpp:43:0,
                 from /usr/include/boost/lexical_cast.hpp:171,
                 from /usr/include/boost/program_options/value_semantic.hpp:14,
                 from /usr/include/boost/program_options/options_description.hpp:13,
                 from /usr/include/boost/program_options.hpp:15,
                 ~/ProgramOptions.cpp:1:
/usr/include/boost/type_traits/detail/has_binary_operator.hpp: In instantiation of ‘const bool boost::detail::has_right_shift_impl::operator_exists<std::basic_istream<wchar_t>, Dataset>::value’:
/usr/include/boost/type_traits/detail/has_binary_operator.hpp:179:4:   instantiated from ‘const bool boost::detail::has_right_shift_impl::trait_impl1<std::basic_istream<wchar_t>, Dataset, boost::detail::has_right_shift_impl::dont_care, false>::value’
/usr/include/boost/type_traits/detail/has_binary_operator.hpp:214:4:   instantiated from ‘const bool boost::detail::has_right_shift_impl::trait_impl<std::basic_istream<wchar_t>, Dataset, boost::detail::has_right_shift_impl::dont_care>::value’
/usr/include/boost/type_traits/detail/has_binary_operator.hpp:221:1:   instantiated from ‘boost::has_right_shift<std::basic_istream<wchar_t>, Dataset, boost::detail::has_right_shift_impl::dont_care>’
/usr/include/boost/lexical_cast.hpp:394:1:   instantiated from ‘boost::detail::deduce_target_char_impl<boost::detail::deduce_character_type_later<Dataset> >’
/usr/include/boost/lexical_cast.hpp:420:89:   instantiated from ‘boost::detail::deduce_target_char<Dataset>’
/usr/include/boost/lexical_cast.hpp:679:92:   instantiated from ‘boost::detail::lexical_cast_stream_traits<std::basic_string<char>, Dataset>’
/usr/include/boost/lexical_cast.hpp:2339:19:   instantiated from ‘static Target boost::detail::lexical_cast_do_cast<Target, Source>::lexical_cast_impl(const Source&) [with Target = Dataset, Source = std::basic_string<char>]’
/usr/include/boost/lexical_cast.hpp:2519:50:   instantiated from ‘Target boost::lexical_cast(const Source&) [with Target = Dataset, Source = std::basic_string<char>]’
/usr/include/boost/program_options/detail/value_semantic.hpp:89:13:   instantiated from ‘void boost::program_options::validate(boost::any&, const std::vector<std::basic_string<charT> >&, T*, long int) [with T = Dataset, charT = char]’
/usr/include/boost/program_options/detail/value_semantic.hpp:170:13:   instantiated from ‘void boost::program_options::typed_value<T, charT>::xparse(boost::any&, const std::vector<std::basic_string<charT> >&) const [with T = Dataset, charT = char]’
~/ProgramOptions.cpp:276:1:   instantiated from here
/usr/include/boost/type_traits/detail/has_binary_operator.hpp:158:4: error: ambiguous overload for ‘operator>>’ in ‘boost::detail::has_right_shift_impl::make [with T = std::basic_istream<wchar_t>]() >> boost::detail::has_right_shift_impl::make [with T = Dataset]()’
/usr/include/boost/type_traits/detail/has_binary_operator.hpp:158:4: note: candidates are:
/usr/include/c++/4.6/istream:122:7: note: std::basic_istream<_CharT, _Traits>::__istream_type& std::basic_istream<_CharT, _Traits>::operator>>(std::basic_istream<_CharT, _Traits>::__istream_type& (*)(std::basic_istream<_CharT, _Traits>::__istream_type&)) [with _CharT = wchar_t, _Traits = std::char_traits<wchar_t>, std::basic_istream<_CharT, _Traits>::__istream_type = std::basic_istream<wchar_t>]
/usr/include/c++/4.6/istream:126:7: note: std::basic_istream<_CharT, _Traits>::__istream_type& std::basic_istream<_CharT, _Traits>::operator>>(std::basic_istream<_CharT, _Traits>::__ios_type& (*)(std::basic_istream<_CharT, _Traits>::__ios_type&)) [with _CharT = wchar_t, _Traits = std::char_traits<wchar_t>, std::basic_istream<_CharT, _Traits>::__istream_type = std::basic_istream<wchar_t>, std::basic_istream<_CharT, _Traits>::__ios_type = std::basic_ios<wchar_t>]
/usr/include/c++/4.6/istream:133:7: note: std::basic_istream<_CharT, _Traits>::__istream_type& std::basic_istream<_CharT, _Traits>::operator>>(std::ios_base& (*)(std::ios_base&)) [with _CharT = wchar_t, _Traits = std::char_traits<wchar_t>, std::basic_istream<_CharT, _Traits>::__istream_type = std::basic_istream<wchar_t>]
/usr/include/c++/4.6/istream:241:7: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(std::basic_istream<_CharT, _Traits>::__streambuf_type*) [with _CharT = wchar_t, _Traits = std::char_traits<wchar_t>, std::basic_istream<_CharT, _Traits>::__streambuf_type = std::basic_streambuf<wchar_t>]
/usr/include/boost/type_traits/detail/has_binary_operator.hpp:70:13: note: boost::detail::has_right_shift_impl::no_operator boost::detail::has_right_shift_impl::operator>>(const boost::detail::has_right_shift_impl::any&, const boost::detail::has_right_shift_impl::any&)

注意:如果我删除了我的 Dataset 类的类型安全,它会编译,即删除

template<typename T>
operator T() const;

更新: 在@Arne Mertz 的出色建议下,我将我的 Dataset 类修改为如下

class Dataset {
public:
  enum Name {
    JAVA, SUMATRA, ASIA, AFRICA
  };

  Dataset(const Name& name = JAVA) : name_(name) {}
  Dataset(const Dataset& dataset) : name_(dataset.name_) {}

  Dataset& operator=(const Dataset& dataset) {name_ = dataset.name_;  return *this; }
  bool operator==(const Dataset& dataset) const { return name_ == dataset.name_; }
  bool operator!=(const Dataset& dataset) const { return name_ != dataset.name_; }

  Name name() const {return name_;}

  std::string asString() const;

private:
  Name name_;
};

我猜这提供了一些我想要的类型安全性,例如:

Dataset d(1); // Should be error
Dataset d = Dataset::JAVA; // Should be fine
double val = d; // Should be error
if(d == Dataset::SUMATRA) {} // should be fine
if(d == 3) {} // Should be error

【问题讨论】:

  • operator T() 到底应该做什么?
  • 将其设为私有可防止从 bool 或 int 自动转换,如 Dataset d = 2;或者如果(d){}。
  • @iNFINITEi oparator T()Dataset 转换为其他内容,而不是相反。例如Dataset d; double x = d; 或在这种情况下,它为任何接受一个参数的函数启用 any_unary_function(d),无论其类型如何 - 因为 Dataset 可以 eb 转换为任何类型。

标签: c++ boost command-line-arguments boost-program-options


【解决方案1】:

模板化的operator T() 是原因,以及编译器如何查找operator&gt;&gt; 的工作原理:

在 boost 的深处,有类似 is &gt;&gt; some_dataset 的东西。由于它在 boost 命名空间中,而不是在您的命名空间中,因此编译器 first 会查看可访问的 boost 命名空间中是否有适当的运算符或 istream 类中的成员运算符,可以调用没有类型转换。显然没有。
作为一种second 方法,它查看是否有这样的操作符可访问 类型转换。输入您的“将我转换为一切”运算符和 istream::operator&gt;&gt; 的不同重载,您在错误消息中将其视为“候选人”。重载接受不同类型的函数指针和 streambuf 指针,并且由于您的类可以转换为其中的 any,编译器不知道是否应将 Dataset 转换为 streambuf* 或其他内容。
作为第三个选项,即当operator T()存在时,编译器会执行参数相关查找(ADL),这意味着它会扫描两个参数的命名空间(即@ 987654328@ 用于istream 和您的命名空间用于Dataset)并在您的命名空间中找到正确的operator&gt;&gt;,因此如果您不声明该转换运算符,它就可以工作。

这是一个完美的例子,说明为什么允许隐式转换可能会出现问题并且应该避免 - 如果它是像您的 operator T() 这样的 anything 的隐式转换,则更是如此。

一种解决方案可能是使转换运算符explicit(仅限C++11),可能更好的是命名转换函数。另一种方法是限制 Dataset 可以通过 std::enable_if 转换为的类型,或者如果只有少数目标类型,则通过显式编写每个非模板转换运算符。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-19
    • 1970-01-01
    • 1970-01-01
    • 2014-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多