如何在C ++中“如果当前语句出错则退出当前函数”

时间:2018-07-31 08:04:53

标签: c++ c logging macros runtime-error

我从一个例子开始阐述我的问题。并在最后给出问题的确切说明。

所以在C语言中,我们可以编写这样的宏

#define NO_ERROR 0
#define RETURN_IF_ERROR(function)                                       \
{                                                                       \
  RetCode = function;                                                   \
  if (RetCode != NO_ERROR)                                              \
  {                                                                     \
      LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) ); \
      return RetCode;                                                   \
  }                                                                     \
  else {                                                                \
      LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );\
  }                                                                     \
}

现在,此宏可以在这样的函数中使用

int ConstructAxes() {
    RETURN_IF_ERROR(GetAxis("alpha"));
    RETURN_IF_ERROR(GetAxis("beta"));
    RETURN_IF_ERROR(GetAxis("gamma"));
    RETURN_IF_ERROR(GetAxis("delta"));
    .
    .
    .
}

因此,我们使用错误代码立即退出当前函数(例如ConstructAxes),如果在其中调用的函数之一返回错误。如果没有该宏,则对于这4条语句中的每条语句,我都必须编写一个if ... else块。我最终得到4行代码,它们显示实际的功能或任务,然后再进行16行错误检查(和/或可选的日志记录)。 (我见过的功能有700多行,只有20〜30行功能,还有600多行if ... else错误检查和记录。那么遵循主要逻辑并不容易。)

(ps,在任何人指出之前,我不能使用异常。这是一个遗留项目,并且不使用也不希望使用异常,我也不是编写异常安全代码的专家。在任何人指出之前,返回的错误代码会在更高级别上重新解释为一些有意义的文本。与该问题无关的地方和方式。

问题是,宏可能有问题,我更喜欢一个函数。那么,在C ++中有没有使用宏的干净而优雅的方法呢?我觉得这是不可能的。

5 个答案:

答案 0 :(得分:2)

好吧,对于嵌入式设备,您可能需要避免使用任何可能需要分配内存的复杂C ++好东西。但是我将必须总是发生的日志记录部分和块短退出分开。

  int test_and_log(int RetCode) {
    if (RetCode != NO_ERROR)
      {
          LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
      }
      else {
          LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
      }
    return RetCode;
  }

然后是一个简约的宏(C语言):

#define RETURN_IF_ERROR(x) { int cr; if ((cr = test_and_log(x)) != NO_ERROR) return cr; }

int ConstructAxes() {
    RETURN_IF_ERROR(GetAxis("beta"));
    RETURN_IF_ERROR(GetAxis("gamma"));
    RETURN_IF_ERROR(GetAxis("delta"));
    .
    .
    .
    return 0;                            // ensure a return value if every line passes
}

但是对于C ++,我仍然会通过抛出int值来使用极简主义异常处理:

  void test_and_log(int RetCode) {
    if (RetCode != NO_ERROR)
      {
          LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
          throw RetCode;
      }
      else {
          LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
      }
  }

然后:

int ConstructAxes() {
  try {
    test_and_log(GetAxis("beta"));
    test_and_log(GetAxis("gamma"));
    test_and_log(GetAxis("delta"));
    .
    .
    .
  }
  catch (int RetCode) {
    return RetCode;
  }
  return 0;                            // ensure a return value if every line passes
}

这是很棘手的,因为最佳实践建议仅抛出std::exception的子类以具有一致的异常处理。但是正如您所说的那样,您不希望在应用程序中使用异常,这是可以接受的。在嵌入式系统上的好处是,从来没有构造任何普通的异常对象。但是请不要在 normal 代码中使用它。


如果您要交换内存以获取处理时间,则始终可以使用test_and_log指定符声明inline ...

答案 1 :(得分:0)

虽然您不能直接做自己想做的事,却可以亲近:

int ConstructAxes() {
    int retCode = NO_ERROR;

    auto fail_if_error = [&retCode](int result) -> bool {
        retCode = result;
        if (retCode != NO_ERROR) {
            LOG(ERROR, "[%d] line [%d] [%s]", retCode, __LINE__, __FILE__);
            return false;
        }
        LOG(VERBOSE, "[%d] line [%d] [%s]", retCode, __LINE__, __FILE__);
        return true;
    };

    fail_if_error(GetAxis("alpha"))
      && fail_if_error(GetAxis("beta"))
      && fail_if_error(GetAxis("gamma"))
      && fail_if_error(GetAxis("delta"));

    return retCode;
}

答案 2 :(得分:0)

由于您似乎正在寻找更多的C解决方案,因此我建议对您现在拥有的唯一改进是在函数中有一个错误处理点(由Felix建议)。然后根据需要执行清理。

#define RETURN_IF_ERROR(function)                                       \
{                                                                       \
  RetCode = function;                                                   \
  if (RetCode != NO_ERROR)                                              \
  {                                                                     \
      goto return_with_error;                                                   \
  }                                                                     \
  else {                                                                \
      LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );\
  }                                                                     \
}

int ConstructAxes() {
    int RetCode;
    RETURN_IF_ERROR(GetAxis("alpha"));
    RETURN_IF_ERROR(GetAxis("beta"));
    RETURN_IF_ERROR(GetAxis("gamma"));
    RETURN_IF_ERROR(GetAxis("delta"));
    .
    .
    .
    return RetCode;
return_with_error:
    cleanup();
    LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
    return RetCode;
}

使用goto进行错误处理和清除in C is fine

答案 3 :(得分:0)

交谈C:

您的方法还可以,如果未正确使用marcos只是一个问题。

因此,我会选择结构合理的do {... break on error ...} while(0)方法。这也有助于您坚持这种模式,即一个函数应只有一个入口点和一个出口点。

为了调试易于阅读 ,我将移动“跳转”语句({{1 }))。超出宏

break

这大致为您提供了1:3的信噪比,您可以通过更改来明显批准

#define NO_ERROR (0)

// log level IDs
#define LL_ERROR (0)
// more log levels here
#define LL_VERBOSE (10)
// more log levels here
#define LL_DEBUG (13)
// more log levels here
#define LL_MAXIMUM (16)

// log level long names
static const char * log_level_names[LL_MAXIMUM] = {
  "error",
  // more here
  "verbose"
  // more here
  "debug"
  // more here
}

int loglevel = LL_ERROR; // default logging-level to error; to be set to any LL_xxxx

// return date-time-stamp string (can be any, and most likely should be ;-)
#define DATETIMESTAMP_STR asctime(localtime(time(NULL)))

// Generic logging
#define LOG(ll, fmt, rc, ...) \
  while (ll <= loglevel) { \
    fprintf(stderr, "%s [%s]: " fmt, DATETIMESTAMP_STR, log_level_name[loglevel], __VA_ARGS__); \
    break; \
  };

// Specific logging
#define LOG_ERROR_OR_VERBOSE(rc, line, fname) \
  do { \
    LOG(NO_ERROR != (rc) ?LL_ERROR :LL_VERBOSE, "[%d] line [%d] [%s]", rc, line, fname); \
  while (0)


int foo(void)
{
  int result = NO_ERROR;

  LOG(LL_DEBUG, "entering '%s'", __func__);

  do {
    result = bar1(...);
    LOG_ERROR_OR_VERBOSE(result, __LINE__, __FILE__);
    if (NO_ERROR <> result) 
      { break; }

    result = bar2(...);
    LOG_ERROR_OR_VERBOSE(result, __LINE__, __FILE__);
    if (NO_ERROR <> result) 
      { break; }

    ...
  } while (0);

  LOG(LL_DEBUG, "leaving '%s' (rc = %d)", __func__, result);

  return result;
}

if (NO_ERROR <> result) 
  { break; }

另一个可能的改进就是更改

if (NO_ERROR <> result) { break; }

result = bar1(...);
LOG_ERROR_OR_VERBOSE(result, __LINE__, __FILE__);

这让您的SNR为1,这是我认为的最佳值::-)

答案 4 :(得分:0)

您可能会编写一个循环,例如:

colnames(mydata) <- c("country", "q1", "q2", "q4", "forestc","hdi", "ef", "hap")

results <- lme(forestc~q1*q2*q4, random=list(~1|ef, ~1|hdi, ~1|hap),data=mydata)

summary(results)

直到我们有std::source_location或类似的int RunFunctions(int line, // __LINE__ const char* file, // __FILE__ std::initializer_list<std::function<int()>> functions) { int counter = 0; for (auto f : functions) { auto RetCode = f(); if (RetCode != NO_ERROR) { LOG(ERROR,"[%d] line [%d] [%s] [%d]", RetCode, line, file, counter )); return RetCode; } else { LOG(VERBOSE,"[%d] line [%d] [%s] [%d]", RetCode, line, file, counter ) ); } ++counter; } return NO_ERROR; } int ConstructAxes() { return RunFunctions(__LINE__, __FILE__, { []() { return GetAxis("alpha"); }, []() { return GetAxis("beta"); }, []() { return GetAxis("gamma"); }, []() { return GetAxis("delta"); } }); } __LINE__

如果不需要捕获,则可以通过函数指针更改__FILE__