dimanche 20 décembre 2015

In C can a called function make the caller function return?

I'd like to find a way to make a function return (with a specific value) the function that called it. Is it 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'm searching for something to put in the /* ... */ part.

Real-life example: the code I'm working one is a function that exports data in a SQLite file. This means a lot of calls to the SQLite API that need their return values checked each time. The result is an awfully looking and far too long function like this where the if (resp_code != SQLITE_OK) part repeats on and 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);
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 */

What I'd like is 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 */

Aucun commentaire:

Enregistrer un commentaire