In C, can a called function return a caller function?

I would like to find a way to make the return function (with a specific value) the function that called it. Is this possible in C? Maybe by checking the call stack?

Abstract example: suppose we have two functions

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

I am looking for something to add a piece /* ... */.

Real life example: the code I'm working with is a function that exports data to an SQLite file. This means many SQLite API calls that check return values ​​each time. The result is a terrible looking and too long function like this, where the part if (resp_code != SQLITE_OK)repeats and continues:

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 */

I would like something like:

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 */

EDIT 2015-12-21: FUZxxl answer , . chux answer ( Rowland Shaw Ziffusion) SQLite , .

!

+4
4

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

- .

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

- 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__)
+1

You can use setjmp()/longjmp()to get this effect, although not quite the way you want:

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

Output:

Hello world!
Failure

This method avoids checking in several places, effectively implementing an exception handling mechanism. However, there are other ways to clear code without resorting to this.

0
source

Source: https://habr.com/ru/post/1621025/


All Articles