如何在C++中抽象出重复的try catch模式

Wil*_*mKF 22 c++ refactoring templates exception

我有一个模式,重复几个成员函数,如下所示:

int myClass::abstract_one(int sig1)
{
  try {
    return _original->abstract_one(sig1);
  } catch (std::exception& err) {
    handleException(err);
  } catch (...) {
    handleException();
  }
}

bool myClass::abstract_two(int sig2)
{
  try {
    return _original->abstract_two(sig2);
  } catch (std::exception& err) {
    handleException(err);
  } catch (...) {
    handleException();
  }
}

[...]

int myClass::abstract_n(bool sig3a, short sig3b)
{
  try {
    return _original->abstract_n(sig3a, sig3b);
  } catch (std::exception& err) {
    handleException(err);
  } catch (...) {
    handleException();
  }
}
Run Code Online (Sandbox Code Playgroud)

其中抽象一个到n是纯虚拟抽象接口用于其的方法myClass_original是具体实现.

我不喜欢这个模式在代码中重复,并希望找到一种方法来消除重复try/ catch模式和代码作为单个抽象,但我想不出一个在没有宏的情况下在C++中执行此操作的好方法.我认为有一种方法可以让模板更好地完成这项工作.

请建议一种简洁的方法来重构此代码以抽象出重复的模式.

Ale*_*ler 24

我问了一个非常相似的概念问题,看看在嵌套的"尝试"中重新抛出异常合法吗?.

基本上,您可以通过捕获所有异常,调用处理程序并重新抛出活动异常,将各种异常处理程序移动到单独的函数.

void handle() {
 try {
  throw;
 } catch (std::exception& err) {
   handleException(err);
 } catch (MyException& err) {
   handleMyException(err);
 } catch (...) {
   handleException();
 }
}

try {
   return _original->abstract_two(sig2);
} catch (...) {
   handle();
}
Run Code Online (Sandbox Code Playgroud)

它可以很好地扩展到更多不同的异常类型以进行区分.try .. catch(...)如果您愿意,可以将第一个包装到宏中:

BEGIN_CATCH_HANDLER
return _original->abstract_two(sig2);
END_CATCH_HANDLER
Run Code Online (Sandbox Code Playgroud)

  • 我听说它叫做"异常调度员成语". (4认同)
  • 我还要补充一点,在SO上问了一次之后,我已经使用了几次,而且我总是觉得它很优雅。所以我想知道这种模式是否有名称? (2认同)

Jam*_*lis 15

如果函数arities数量有限,一个选项是使用函数模板:

template <typename ReturnT, typename ClassT>
ReturnT call_and_handle(ClassT* obj, ReturnT(ClassT::*func)()) 
{
    try {
        return (obj->*func)();
    }
    catch (const std::exception& ex) {
        handleException(ex);
    }
    catch (...) {
        handleException();
    }
    return ReturnT();
}
Run Code Online (Sandbox Code Playgroud)

这假设handleException是一些非成员函数,但如果它是成员函数,则很容易修改它.call_and_handle如果处理异常,您需要决定返回什么; 我让它返回初始化ReturnT为占位符.

这会将您的成员函数减少为:

int myClass::abstract_one()
{
    return call_and_handle(_original, &myClass::abstract_one);
}
Run Code Online (Sandbox Code Playgroud)

您需要一个单独的函数模板来调用具有一个参数,两个参数等的函数.

如果你的函数具有大量的参数并且你真的很绝望,你可以使用一个宏(我不会真的推荐这个):

#define CALL_AND_HANDLE(expr)           \
    try {                               \
        return (expr);                  \
    }                                   \
    catch (const std::exception& ex) {  \
        handleException(ex);            \
    }                                   \
    catch (...) {                       \
        handleException();              \
    }
Run Code Online (Sandbox Code Playgroud)

哪个可以用作:

int myClass::abstract_one()
{
    CALL_AND_HANDLE(_original->myClass::abstract_one());
}
Run Code Online (Sandbox Code Playgroud)

catch (...)顺便说一句,如果你不重新抛出被捕获的异常,你应该在大多数情况下终止该程序.