【问题标题】:In C can a called function make the caller function return?在C中,被调用函数可以使调用函数返回吗?
【发布时间】:2016-03-26 22:59:57
【问题描述】:

我想找到一种方法让函数返回(具有特定值)调用它的函数。在C中可能吗?也许通过检查调用堆栈?

抽象例子:假设我们有两个函数

int called() {
    if (some_check_fails()) {
        /* here make caller() return -1 so "Hello world!\n" is not printed */
    }
}

int caller() {
    called();
    printf("Hello world!\n");
    return 0;
}

我正在寻找要放入 /* ... */ 部分的内容。

现实生活中的例子:我正在使用的代码是一个在 SQLite 文件中导出数据的函数。这意味着对 SQLite API 的大量调用需要每次检查它们的返回值。结果是一个看起来很糟糕而且太长的函数,就像这样if (resp_code != SQLITE_OK) 部分不断重复:

sqlite3 *db;
char *err_msg;

/* Open the database in read-write mode, create it if not exists yet */
int resp_code = sqlite3_open_v2(filename, &db,
                                SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
                                NULL);
if (resp_code != SQLITE_OK) {
    fprintf(stderr, "SQLite error: cannot open database %s, %s\n", filename,
            sqlite3_errmsg(db));
    sqlite3_close(db);
    return OMG_ERROR_HERE;
}

/* Create table */
char *query_table = "DROP TABLE IF EXISTS sometable; "
                    "CREATE TABLE sometable "
                    "(value int, data TEXT);";
resp_code = sqlite3_exec(db, query_table, 0, 0, &err_msg);
if (resp_code != SQLITE_OK) {
    fprintf(stderr, "SQLite error: %s\n", err_msg);
    sqlite3_free(err_msg);
    sqlite3_close(db);
    return OMG_ERROR_HERE;
}

/* Prepare statement */
char *query = "INSERT INTO sometable VALUES (@i, @s);";
sqlite3_stmt *stmt;
resp_code = sqlite3_prepare_v2(db, query, 150, &stmt, NULL);
if (resp_code != SQLITE_OK) {
    fprintf(stderr, "SQLite error: %s\n", err_msg);
    sqlite3_free(err_msg);
    sqlite3_close(db);
    return OMG_ERROR_HERE;
}

/* Start transaction */
resp_code = sqlite3_exec(db, "BEGIN TRANSACTION", 0, 0, &err_msg);
if (resp_code != SQLITE_OK) {
    fprintf(stderr, "SQLite error: %s\n", err_msg);
    sqlite3_free(err_msg);
    sqlite3_close(db);
    return OMG_ERROR_HERE;
}

/* AND SO ON */

我想要的是这样的:

sqlite3 *db;
char *err_msg;

/* Open the database in read-write mode, create it if not exists yet */
int resp_code = sqlite3_open_v2(filename, &db,
                                SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
                                NULL);
return_this_function_if_not_ok(resp_code);

/* Create table */
char *query_table = "DROP TABLE IF EXISTS sometable; "
                    "CREATE TABLE sometable "
                    "(value int, data TEXT);";
resp_code = sqlite3_exec(db, query_table, 0, 0, &err_msg);
return_this_function_if_not_ok(resp_code);

/* Prepare statement */
char *query = "INSERT INTO sometable VALUES (@i, @s);";
sqlite3_stmt *stmt;
resp_code = sqlite3_prepare_v2(db, query, 150, &stmt, NULL);
return_this_function_if_not_ok(resp_code);

/* Start transaction */
resp_code = sqlite3_exec(db, "BEGIN TRANSACTION", 0, 0, &err_msg);
return_this_function_if_not_ok(resp_code);

/* AND SO ON */

编辑 2015-12-21:我选择FUZxxl's answer 是最好的,因为它是唯一真正回答我关于返回调用函数的问题的人。另一方面,chux's answer(基于Rowland Shaw'sZiffusion's)正在以我喜欢的方式解决我的SQLite 问题。

非常感谢大家!!

【问题讨论】:

  • 您可以使用宏来让预处理器为您编写错误检查代码
  • 是什么阻止你使用返回值?或者您是否正在寻找某种方法来创建动态回调?
  • 如果你今天感觉特别受虐,你可以看看setjmplongjmp
  • 顺便说一句:这就是异常被发明来解决的问题。

标签: c sqlite return callstack


【解决方案1】:

这样的事情在 C 中是不可能的,但你可以接近。

标识符__func__在每个函数的开头隐式声明为

static const char __func__[];

它的值是当前函数的名称作为字符串。您可以编写一个类似函数的宏,将调用者的名称隐式传递给被调用者。如果应该接收调用者名称的函数类似于:

void error_check_fun(const char *function, int code, int result);

你可以这样写一个宏:

#define error_check(code, result) error_check_fun(__func__, code, result);

同样,__FILE____LINE__ 是分别展开到当前源文件和行的宏。

【讨论】:

    【解决方案2】:

    您可以使用setjmp()/longjmp() 来获得此效果,尽管与您想要的不完全一样:

    #include <setjmp.h>
    #include <stdio.h>
    
    
    int check_result;
    
    int some_check_fails() {
        return check_result;
    }
    
    int called(jmp_buf buf) {
        if (some_check_fails()) {
            longjmp(buf,1);
        }
    }
    
    int caller() {
        jmp_buf buf;
        if (setjmp(buf)==0) {
            called(buf);
            printf("Hello world!\n");
            return 0;
        }
        else {
            printf("Failure\n");
            return -1;
        }
    }
    
    int main() {
        check_result = 0;
        caller();
        check_result = 1;
        caller();
    }
    

    输出:

    你好世界! 失败

    这种技术确实避免了在多个地方进行检查,有效地实现了一种异常处理机制。但是,还有其他方法可以在不诉诸于此的情况下清理代码。

    【讨论】:

      【解决方案3】:

      你可以试试这样的。

      #define return_this_function_if_not_ok(db, sql_code, sql_msg, code) \
          if ((sql_code) != SQLITE_OK) { \
              fprintf(stderr, "SQLite error: %s\n", (*sql_msg)); \
              sqlite3_free(sql_msg); \
              sqlite3_close(db); \
              return (code); \
          }
      
      sqlite3 *db;
      char *err_msg;
      
      /* Open the database in read-write mode, create it if not exists yet */
      int resp_code = sqlite3_open_v2(filename, &db,
                                      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
                                      &err_msg);
      return_this_function_if_not_ok(db, resp_code, err_msg, OMG_ERROR_HERE);
      

      【讨论】:

        【解决方案4】:

        推荐Rowland Shaw@Ziffusion之类的东西

        调用一个将数据打包并处理常见内务处理的函数。

        int foo(char *err_msg, int code) {
          if (msg) {
            fprintf(stderr, "SQLite error: %s\n", err_msg);
            sqlite3_free(err_msg);
          } else {
            fprintf(stderr, "SQLite error: %s\n", "Default error message");
          }
          sqlite3_close(db);
          return code;
        }
        
        resp_code = sqlite3_exec(...);
        if (resp_code != SQLITE_OK) return foo(err_msg, OMG_ERROR_HERE);
        ...
        resp_code = sqlite3_prepare_v2(db, query, 150, &stmt, NULL);
        if (resp_code != SQLITE_OK) return foo(NULL, OMG_ERROR_HERE);
        

        进一步建议,包括文件和行号。这是我发现非常有用的东西。

        int bar(char *err_msg, int code, const char *file, int line) {
          fprintf(stderr, "SQLite error:%s, Code:%d, File:%s, Line%d\n",
             err_msg ? err_msg : "Default error message", code, file, line);
          }
          sqlite3_free(err_msg);
          sqlite3_close(db);
          return code;
        }
        
        #define foo(err_msg, code, file, line) bar((err_msg), (code), __FILE__, __LINE__)
        

        【讨论】:

        • 这个答案实际上最容易解决我的丑陋代码问题,非常感谢:) 但它没有直接回答关于返回调用函数的问题,所以我不能选择它作为答案。
        猜你喜欢
        • 1970-01-01
        • 2022-12-04
        • 2017-03-22
        • 1970-01-01
        • 2010-09-20
        • 1970-01-01
        • 2013-11-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多