如果有一些框架期望像这样的回调
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块?
答案 0 :(得分:6)
利用以下事实,即当您处于catch块中时,您会遇到一个“当前处理的异常”,您可以再次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函数。它将错误转换逻辑集中到一个位置,因此您可以轻松地将一个函数与另一个处理程序进行扩展,并将其转换为使用此实用程序的所有函数。
答案 1 :(得分:2)
写一个包装为您做翻译。
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>
。
(这与您的答案基本相同,但具有独立功能而不是静态成员)。
答案 2 :(得分:0)
这是基于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