在 C 中,被调用函数可以使调用函数 return 吗?
In C can a called function make the caller function return?
我想找到一种方法使函数 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 的大量调用需要每次 检查它们的 return 值 。结果是一个看起来很糟糕且太长的函数,就像这样 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:我选择 as best because it's the only one actually answering my question about returning the caller function. On the other hand (based on and ) 正在以我喜欢的方式解决我的 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__
分别是扩展到当前源文件和行的宏。
您可以使用 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
这种技术确实避免了在多个地方进行检查,有效地实现了一种异常处理机制。但是,还有其他方法可以在不诉诸于此的情况下清理代码。
你可以试试这个。
#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);
推荐 and
调用一个函数来打包数据并处理常见的内务处理。
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__)
我想找到一种方法使函数 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 的大量调用需要每次 检查它们的 return 值 。结果是一个看起来很糟糕且太长的函数,就像这样 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:我选择
大大大大感谢大家!!
这样的事情在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__
分别是扩展到当前源文件和行的宏。
您可以使用 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
这种技术确实避免了在多个地方进行检查,有效地实现了一种异常处理机制。但是,还有其他方法可以在不诉诸于此的情况下清理代码。
你可以试试这个。
#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);
推荐
调用一个函数来打包数据并处理常见的内务处理。
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__)