Boost Lexical Cast 包装器





5.00/5 (1投票)
Boost Lexical Cast 包装器,
不友好的异常信息
#include <iostream>
#include <stdexcept>
#include <boost/lexical_cast.hpp>
int main()
{
const char* str = "aa";
try
{
int result = boost::lexical_cast<int>(str);
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
以下是显示的异常信息,如果涉及多个 lexical_cast
调用,我们无法从该信息中得知哪个类型转换失败。
bad lexical cast: source type value could not be interpreted as target
让我们使用 std::string
类型再次尝试。
#include <string>
#include <iostream>
#include <stdexcept>
#include <boost/lexical_cast.hpp>
int main()
{
const std::string str = "aa";
try
{
int result = boost::lexical_cast<int>(str);
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
使用 std::string
类型时,显示相同的异常信息。
bad lexical cast: source type value could not be interpreted as target
包装 lexical_cast 以获取函数签名
让我们包装 lexical_cast
,使其抛出带有函数签名信息的 std::runtime_error
。
#include <string>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <boost/lexical_cast.hpp>
#ifdef _WIN32
#define MY_FUNC_SIG __FUNCSIG__
#else
#define MY_FUNC_SIG __PRETTY_FUNCTION__
#endif
template <typename Target, typename Source>
inline Target lexical_cast_wrapper(const Source &arg)
{
Target result;
try
{
result = boost::lexical_cast<Target>(arg);
}
catch (boost::bad_lexical_cast&)
{
std::ostringstream oss;
oss << "bad_lexical_cast exception thrown:\n";
oss << "Source arg:<" << arg << ">\n";
oss << "Function sig:" << MY_FUNC_SIG;
throw std::runtime_error(oss.str().c_str());
}
return result;
}
int main()
{
const std::string str = "aa";
try
{
int result = lexical_cast_wrapper<int>(str);
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
来自 VC++ 的 const char*
源参数的输出。
int __cdecl lexical_cast_wrapper<int,const char*>(const char *const &)
来自 GCC 和 Clang 的 const char*
源参数的输出。
Target lexical_cast_wrapper(const Source&) [with Target = int; Source = const char*]
来自 VC++ 的 std::string
源参数的输出。
int __cdecl lexical_cast_wrapper<int,class std::basic_string<char,struct std::char_traits<char,class std::allocator<char> >>(const class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &)
来自 GCC 和 Clang 的 std::string
源参数的输出。
Target lexical_cast_wrapper(const Source&) [with Target = int; Source = std::basic_string<char>]
下一步是手动编写 2 个解析器(1 个用于 VC++,另一个用于 GCC/Clang)来提取源参数和目标参数类型。
#include <string>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <stack>
#include <boost/lexical_cast.hpp>
#ifdef _WIN32
#define MY_FUNC_SIG __FUNCSIG__
#else
#define MY_FUNC_SIG __PRETTY_FUNCTION__
#endif
#ifdef _WIN32
std::string replace_string_type(const std::string& long_str_type)
{
std::string result = (long_str_type.find("const") != std::string::npos) ? "const " : "";
if (long_str_type.find("std::basic_string<char") != std::string::npos)
result += "std::string";
else if (long_str_type.find("std::basic_string<wchar_t") != std::string::npos)
result += "std::wstring";
else if (long_str_type.find("std::basic_string<char16_t") != std::string::npos)
result += "std::u16string";
else if (long_str_type.find("std::basic_string<char32_t") != std::string::npos)
result += "std::u32string";
else if (long_str_type.find("std::pmr::basic_string<char") != std::string::npos)
result += "std::pmr::string";
else if (long_str_type.find("std::pmr::basic_string<wchar_t") != std::string::npos)
result += "std::pmr::wstring";
else if (long_str_type.find("std::pmr::basic_string<char16_t") != std::string::npos)
result += "std::pmr::u16string";
else if (long_str_type.find("std::pmr::basic_string<char32_t") != std::string::npos)
result += "std::pmr::u32string";
else
result = long_str_type;
return result;
}
bool getTypes(const std::string& func, std::string& src_type, std::string& target_type)
{
src_type = "";
target_type = "";
static const std::string func_prelude = "lexical_cast_wrapper<";
size_t pos = func.find(func_prelude);
if (pos != std::string::npos)
{
pos += func_prelude.size();
std::stack<bool> bstack; // type of the stack does not matter
bstack.push(true);
bool parsing_target = true;
while (pos < func.size())
{
if (func[pos] == '<')
bstack.push(true);
else if (func[pos] == '>')
{
bstack.pop();
if (bstack.size() == 0)
break;
}
if (func[pos] == ','&&bstack.size() == 1)
{
parsing_target = false;
++pos;
continue;
}
if (parsing_target)
target_type += func[pos];
else
src_type += func[pos];
++pos;
}
src_type = replace_string_type(src_type);
target_type = replace_string_type(target_type);
return true;
}
return false;
}
#else
bool getTypes(const std::string& func, std::string& src_type, std::string& target_type)
{
src_type = "";
target_type = "";
static const std::string prelude = "[with Target = ";
static const std::string prelude2 = "; Source = ";
size_t pos = func.find(prelude);
if (pos != std::string::npos)
{
pos += prelude.size();
size_t pos2 = func.find(prelude2, pos);
if (pos2 != std::string::npos)
{
target_type = func.substr(pos, pos2 - pos);
pos2 += prelude2.size();
size_t posEnd = func.find("]", pos2);
if (posEnd != std::string::npos)
{
src_type = func.substr(pos2, posEnd - pos2);
return true;
}
}
}
return false;
}
#endif
template <typename Target, typename Source>
inline Target lexical_cast_wrapper(const Source &arg)
{
Target result;
try
{
result = boost::lexical_cast<Target>(arg);
}
catch (boost::bad_lexical_cast&)
{
std::string src_type = ""; std::string target_type = "";
if (getTypes(MY_FUNC_SIG, src_type, target_type))
{
std::ostringstream oss;
oss << "bad_lexical_cast exception thrown:";
oss << "\nSource arg:" << arg;
oss << "\nSource type:" << src_type;
oss << "\nTarget type:" << target_type;
throw std::runtime_error(oss.str().c_str());
}
}
return result;
}
int main()
{
//const char* str = "aa";
const std::string str = "aa";
try
{
int result = lexical_cast_wrapper<int>(str);
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
对于 VC++ 输出,我选择显示 typedef'ed 类型,而不是完整的类型信息。对于 GCC 和 Clang,我按原样显示类型。输出如下所示。
VC++ bad_lexical_cast exception thrown: Source arg:aa Source type:std::string Target type:int
GCC and Clang bad_lexical_cast exception thrown: Source arg:aa Source type:std::basic_string<char> Target type:int
注意:lexical_cast_wrapper
抛出 std::runtime_error
,而不是 bad_lexical_cast
。
如果 Boost lexical_cast
的开发者能够在 lexical_cast
内部实现此解析,那就太好了。对于我的用例,需要解析一个包含 50 行 10 列的配置文件,这个包装器可以帮助快速缩小问题范围。此包装器只有在出现异常的情况下才会产生较高的开销。如果开发者只需要在测试期间使用它,可以定义一个 LEX_CAST
宏来相应地定向调用,并且必须将捕获的异常类型从 bad_lexical_cast
更改为 std::exception
。
#ifdef _DEBUG
#define LEX_CAST lexical_cast_wrapper
#else
#define LEX_CAST boost::lexical_cast
#endif