将异常从一种类型转换为另一种类型
Translate exception from one type to another
如果有一些框架需要像
这样的回调类型
void fcn(F& data);
它可以处理ExF类型的异常。
在我的回调中,我使用了一些第三方库来抛出 ExL 类型的异常。所以我的回调看起来像
void fcn1(F& data)
{
try
{
// call library
}
catch(const ExL& ex)
{
ExF exf = make_ExF(ex);
throw exf;
}
}
现在我想编写更多回调 fcn2、fcn3...,它们使用库但不想一直重复相同的 try/catch。特别是,也许我会添加另一个
catch(const ExL2& ex)
将来会阻止几个回调。我无法更改框架和库中的代码(尤其是异常类型)。如何避免重复 try/catch 块?
利用这个事实,当你在一个 catch 块中时,你有一个 "currently handled exception",你可以再次 throw;
。这允许您将逻辑移动到另一个函数中
void except_translate() {
try
{
throw;
}
catch(const ExL& ex)
{
ExF exf = make_ExF(ex);
throw exf;
}
}
void fcn1(F& data)
{
try
{
// call library
}
catch(...)
{
except_translate();
}
}
这种技术被称为(用于谷歌搜索)作为 Lippincott 函数。它将错误转换逻辑集中到一个地方,因此您可以轻松地使用另一个处理程序扩展该函数,并且它将为使用该实用程序的所有函数转换它。
编写一个包装器来为您进行翻译。
template <typename Func, typename ... Args>
decltype(auto) translate(Func func, Args&&... args)
{
try {
return func(std::forward<Args>(args)...);
}
catch(const ExL& ex) {
ExF exf = make_ExF(ex);
throw exf;
}
}
现在您可以 F data; translate(fcn, data)
并且其工作方式与 fcn1(data)
相同。
EDIT 以上代码不能用作回调,除非进一步包装它(例如在 lambda 中)。这是另一种方法:
template <typename Res, typename ... Args>
auto
translate(Res (&func)(Args...)) ->
std::function<Res(Args...)>
{
try {
return [&](Args&& ... args) { return func(std::forward<Args>(args)...); };
}
catch(const ExL& ex) {
ExF exf = make_ExF(ex);
throw exf;
}
}
那么你的回电是translate(fcn)
。
这两个都很通用,如果你只需要包装一种类型的回调,这里有一个简单的方法:
template<void FCT(F& data)>
void translate(F& data)
{
try {
FCT(data);
}
catch(const ExL& ex) {
ExF exf = make_ExF(ex);
throw exf;
}
}
回调是translate<fcn>
.
(这与您的答案基本相同,但使用独立函数而不是静态成员)。
这是基于n.m思想的解决方案。:
void fcn1_inner(F& data)
{
// call library, no try/catch
}
template<void FCT(F& data)> struct Wrapper
{
static void FN(F& data)
{
try
{
FCT(data);
}
catch(const ExL& ex)
{
ExF exf = make_ExF(ex);
throw exf;
}
}
};
回调中
Wrapper<fcn1_inner>::FN
如果有一些框架需要像
这样的回调类型void fcn(F& data);
它可以处理ExF类型的异常。
在我的回调中,我使用了一些第三方库来抛出 ExL 类型的异常。所以我的回调看起来像
void fcn1(F& data)
{
try
{
// call library
}
catch(const ExL& ex)
{
ExF exf = make_ExF(ex);
throw exf;
}
}
现在我想编写更多回调 fcn2、fcn3...,它们使用库但不想一直重复相同的 try/catch。特别是,也许我会添加另一个
catch(const ExL2& ex)
将来会阻止几个回调。我无法更改框架和库中的代码(尤其是异常类型)。如何避免重复 try/catch 块?
利用这个事实,当你在一个 catch 块中时,你有一个 "currently handled exception",你可以再次 throw;
。这允许您将逻辑移动到另一个函数中
void except_translate() {
try
{
throw;
}
catch(const ExL& ex)
{
ExF exf = make_ExF(ex);
throw exf;
}
}
void fcn1(F& data)
{
try
{
// call library
}
catch(...)
{
except_translate();
}
}
这种技术被称为(用于谷歌搜索)作为 Lippincott 函数。它将错误转换逻辑集中到一个地方,因此您可以轻松地使用另一个处理程序扩展该函数,并且它将为使用该实用程序的所有函数转换它。
编写一个包装器来为您进行翻译。
template <typename Func, typename ... Args>
decltype(auto) translate(Func func, Args&&... args)
{
try {
return func(std::forward<Args>(args)...);
}
catch(const ExL& ex) {
ExF exf = make_ExF(ex);
throw exf;
}
}
现在您可以 F data; translate(fcn, data)
并且其工作方式与 fcn1(data)
相同。
EDIT 以上代码不能用作回调,除非进一步包装它(例如在 lambda 中)。这是另一种方法:
template <typename Res, typename ... Args>
auto
translate(Res (&func)(Args...)) ->
std::function<Res(Args...)>
{
try {
return [&](Args&& ... args) { return func(std::forward<Args>(args)...); };
}
catch(const ExL& ex) {
ExF exf = make_ExF(ex);
throw exf;
}
}
那么你的回电是translate(fcn)
。
这两个都很通用,如果你只需要包装一种类型的回调,这里有一个简单的方法:
template<void FCT(F& data)>
void translate(F& data)
{
try {
FCT(data);
}
catch(const ExL& ex) {
ExF exf = make_ExF(ex);
throw exf;
}
}
回调是translate<fcn>
.
(这与您的答案基本相同,但使用独立函数而不是静态成员)。
这是基于n.m思想的解决方案。:
void fcn1_inner(F& data)
{
// call library, no try/catch
}
template<void FCT(F& data)> struct Wrapper
{
static void FN(F& data)
{
try
{
FCT(data);
}
catch(const ExL& ex)
{
ExF exf = make_ExF(ex);
throw exf;
}
}
};
回调中
Wrapper<fcn1_inner>::FN