我想找到一种方法,让函数返回(带有特定值)调用它的函数。这在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, %sn", 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: %sn", 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: %sn", 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: %sn", 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 Shaw和Ziffusion的)是以我喜欢的方式解决我的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: %sn", (*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: %sn", err_msg);
sqlite3_free(err_msg);
} else {
fprintf(stderr, "SQLite error: %sn", "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%dn",
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("Failuren");
return -1;
}
}
int main() {
check_result = 0;
caller();
check_result = 1;
caller();
}
输出:
你好,世界!失败
这种技术确实避免了将检查放在多个位置,从而有效地实现了一种类型的异常处理机制。然而,还有其他方法可以在不使用此方法的情况下清理代码。