代码之家  ›  专栏  ›  技术社区  ›  Duck Dodgers

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

  •  0
  • Duck Dodgers  · 技术社区  · 6 年前

    我从一个例子开始阐述我的问题。最后以问题的确切陈述结束。

    在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"));
        .
        .
        .
    }
    

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

    (另外,在有人指出之前,我不能使用例外。这是一个遗留项目,不使用异常,也不希望使用异常,我也不是编写异常安全代码的专家。同样,在任何人指出之前,返回的错误代码在更高的层次上被重新解释为一些有意义的文本。在哪里以及如何与手头的这个问题无关。)

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

    5 回复  |  直到 6 年前
        1
  •  0
  •   Jarod42    6 年前

    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"); }
        });
    }
    

    std::source_location __LINE__ __FILE__

    std::function

        2
  •  2
  •   Serge Ballesta    6 年前

      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;
      }
    

    #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
    }
    

      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


    test_and_log inline

        3
  •  0
  •   Sander De Dycker    6 年前

    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;
    }
    
        4
  •  0
  •   mnistic    6 年前

    #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;
    }
    

    in C is fine

        5
  •  0
  •   alk    6 年前

    do {... break on error ...} while(0)

    break

    #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__);
    

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