在说boost之前,我们先来看一个小工具,这是一个关于字符转换的工具,他可以取代c库中的atoi这些小函数,但同时他更加安全。
为了实现更加安全和更具有鲁棒性的转换,首先我们写一个自己的异常类,这个异常类主要是针对字符转换,所以我们从std::bad_cast来继承他。
//------------------------------------
// 顾名思义,该类主要是帮助使用者
// 提供一些转换失败的信息
//------------------------------------
class bad_lexical_cast : public std::bad_cast
{
public:
bad_lexical_cast() :
source(&typeid(void)), target(&typeid(void))
{
}
bad_lexical_cast(
const std::type_info &source_type,
const std::type_info &target_type) :
source(&source_type), target(&target_type)
{
}
const std::type_info &source_type() const
{
return *source;
}
const std::type_info &target_type() const
{
return *target;
}
virtual const char *what() const throw()
{
return "bad lexical cast: source type value could not be interpreted as target";
}
virtual ~bad_lexical_cast() throw()
{
}
private:
const std::type_info *source;
const std::type_info *target;
};
//----------------------------------------
// 当我们转换失败的时候就会抛出上面的异常
//----------------------------------------
接下来我们写一个转换的核心类,在C++里面有个stringstream流,他能够将各种带有operator<<操作符的类型转换为string,同时也能够将string转换带有operator>>操作符的类型,他的用法如下:
//--------------------------------------------
// 这里举两个例子
// 一个是将其他类型转换为string或者是wstring
//-------------------------------------------
template<typename T>
std::wstring ToString(const T& s)
{
std::ostringstream oss;
oss << s;
return oss.str();
}
template<typename T>
std::wstring ToWString(const T& s)
{
std::wostringstream oss;
oss << s;
return oss.str();
}
template<typename T>
T FromWString(const std::wstring& s)
{
T x;
std::wistringstream iss(s);
iss >> x;
return x;
}
template<typename T>
T FromString(const std::string& s)
{
T x;
std::istringstream iss(s);
iss >> x;
return x;
}
其实,到这里我觉得我们已经可以很方便的转换字符问题了,但是,我们还可以做得更好一些,我们来定义一个字符转换的类:
//----------------------------------------
// class lexical_stream
//----------------------------------------
template<typename Target, typename Source>
class lexical_stream
{
private:
typedef char char_type;
std::basic_stringstream<char_type> stream;
public:
lexical_stream()
{
stream.unsetf(std::ios::skipws);
if(std::numeric_limits<Target>::is_specialized)
stream.precision(std::numeric_limits<Target>::digits10 + 1);
else if(std::numeric_limits<Source>::is_specialized)
stream.precision(std::numeric_limits<Source>::digits10 + 1);
}
~lexical_stream()
{
}
//把Source类型输入到流中
bool operator<<(const Source &input)
{
return !(stream << input).fail();
}
//把流转换为Target类型输出
template<typename InputStreamable>
bool operator>>(InputStreamable &output)
{
return !std::is_pointer<InputStreamable>::value &&
stream >> output &&
stream.get() ==
std::char_traits<char_type>::eof();
}
// string特化
template<>
bool operator>>(std::string &output)
{
output = stream.str();
return true;
}
};
//----------------------------------------
我们不难看出,其实这就是将上面我们的小例子更进一步的封装,同时添加了安全安全监测和精度的设置,还特别针对string类型进行了特化,这有一个好处就是当我们string转换到string的时候就变得相当的高效了。
接下来我们要处理数组和指针之间的问题:
//-----------------------------------------
// 将Source重新包装
// 因为在lexical_stream<Target, NewSource>
// 构造函数中会对两个类型进行判断
// 所以有了下面的两个模板
// 当我们这样:
// char ch[] = "123456";
// int k = lexical_cast<int>(ch);
// 写的时候,他会根据ch数组通过
// template<class T, std::size_t N>
// 等价出ch的类型为char*
// 否则是无法通过编译的
// 这就是将数组退化到指针的方式
//-----------------------------------------
template<class T>
struct array_to_pointer_decay
{
typedef T type;
};
template<class T, std::size_t N>
struct array_to_pointer_decay<T[N]>
{
typedef const T* type;
};
有了这些东西后,我们就可以进一步提取让他变得更加容易使用:
//----------------------------------------
// lexical_cast
//----------------------------------------
template<typename Target, typename Source>
Target lexical_cast(const Source &arg)
{
typedef typename array_to_pointer_decay<Source>::type NewSource;
lexical_stream<Target, NewSource> interpreter;
Target result;
if(!(interpreter << arg && interpreter >> result))
throw(bad_lexical_cast(typeid(NewSource), typeid(Target)));
return result;
}
//----------------------------------------
// 下面是演示一下该工具的释放方式
//----------------------------------------
int main(){
char ch[] = "123456";
int k = lexical_cast<int>(ch);
try{
int i = lexical_cast<int>("4365");
float f = lexical_cast<float>("234.546");
double d = lexical_cast<double>("24534.546345");
std::string s = lexical_cast<std::string>(24534.546345);
std::cout << i << std::endl;
std::cout << f << std::endl;
std::cout << d << std::endl;
std::cout << s << std::endl;
}
catch (bad_lexical_cast& e){
std::cout << e.what() << std::endl;
}
try{
lexical_cast<int>("0.335");
}
catch (bad_lexical_cast& e){
std::cout << "source type: " << e.source_type().name() << std::endl;
std::cout << "target type: " << e.target_type().name() << std::endl;
std::cout << e.what() << std::endl;
}
return 0;
}
那么到这里,我想这一讲要说的内容其实已经完成了,因为我们这一讲本来就是要说的是boost::lexical_cast,而boost::lexical_cast的实现方式就和我们上面所说的是一样的,唯一不同的是如果我们要使用boost的lexical_cast那么我们要包含头文件<boost/lexical_cast.hpp>,我们要捕获的异常为boost::bad_lexical_cast。
在源码都熟悉以后用是不是也就不需要我再说了呢?
ok,这一讲就到这里吧,接下来我们都会说C++对字符串的操作,包括正则表达式。