繁体   English   中英

在C中,一个被调用的函数能使调用函数返回吗?

[英]In C can a called function make the caller function return?

我想找到一种方法来使函数返回(具有特定值)调用它的函数。 在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)部分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的答案是最好的,因为它是唯一一个实际回答我关于返回调用函数的问题。 另一方面, chux的答案 (基于Rowland ShawZiffusion的 )正在以我喜欢的方式解决我的SQLite问题。

非常感谢各位!

这样的事情在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__是分别扩展到当前源文件和行的宏。

你可以尝试这样的事情。

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

推荐像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__)

您可以使用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();
}

输出:

Hello world!
Failure

这种技术确实避免将检查放在多个位置,有效地实现了一种异常处理机制。 但是,还有其他方法可以清理代码而不需要使用它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM