iNF*_*TEi 3 c++ boost command-line-arguments boost-program-options
我试图使用Boost ProgramOptions来解析配置文件以初始化我自己的类类型Dataset(下面的代码)
我将选项添加为:
config_.add_options()("dataset", po::value<Dataset>()->required(),"Dataset");
Run Code Online (Sandbox Code Playgroud)
我的数据集类定义如下:
#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_
Run Code Online (Sandbox Code Playgroud)
我以为我已经做了所有事情,至少这是为了让我的其他自定义类型更快乐.但是我得到了这个编译错误,我无法理解:
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&)
Run Code Online (Sandbox Code Playgroud)
注意:如果我删除了我的数据集类的类型安全性,它会编译,即删除
template<typename T>
operator T() const;
Run Code Online (Sandbox Code Playgroud)
更新: 在@Arne Mertz的优秀建议下,我修改了我的数据集类如下
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_;
};
Run Code Online (Sandbox Code Playgroud)
我想这提供了一些我想要的类型安全性,如:
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
Run Code Online (Sandbox Code Playgroud)
模板operator T()是原因,以及编译器如何查找以下内容的工作原理operator>>:
在激励的内心深处,有类似的东西is >> some_dataset.由于它位于boost名称空间中,而不在您的名称空间中,因此编译器首先查看可访问的boost名称空间中是否存在正确的运算符,或者是否在istream类中有成员运算符,可以在没有类型转换的情况下调用它.显然没有.
作为第二种方法,它查看是否存在可通过类型转换访问的此类运算符.输入您的"将我转换为所有内容"操作符,并istream::operator>>在错误消息中将您看作"候选者" 的不同重载.重载接受不同类型的函数指针和streambuf指针,因为您的类可以转换为any其中,编译器不知道是否应将数据集转换为a streambuf*或其他内容.
作为第三个选择,即当operator T()是不存在的编译器参数依赖查找(ADL),这意味着它可以扫描参数的命名空间(即namespace std对istream你的命名空间的Dataset),并且得到适当operator>>的命名空间,所以它如果您没有声明转换运算符,则有效.
这是一个很好的例子,说明为什么允许隐式转换可能会出现问题并且应该避免 - 如果它是隐式转换为类似你的东西,那就更好了operator T().
一种解决方案可能是制作转换运算符explicit(仅限C++ 11),可能更好的是命名转换函数.另一种方法是限制Dataset可以转换为vía 的类型std::enable_if,或者如果只有少数目标类型,则通过显式写入每个非模板化转换运算符.