Reputation: 29
I know there is a lot of posts and documentation out there on exactly how to use this function, but no matter what I do I can't quite grasp the concept of callback
functions in regards to this API. So here's what I'm trying to do, I have a relation that looks like this:
+----+-------+
| id | event |
+----+-------+
| 1 | 45 |
| 2 | 22 |
| 3 | 12 |
+----+-------+
My goal is to use the following SQL statement SELECT MAX(id) FROM events;
to get the value 3
back for later use in my C Code. I'm not exactly sure how to use sqlite3_exec()
to get this single value. I'll leave my currently written code below to show where I'm currently at. Btw I'm not including my code to open the database because I know that works. Callback is empty because I'm not exactly sure how to use it. Thanks in advance for any help! (Please go easy on me, I'm trying to get community help to learn :))
Current sqlite3_exec() call
int max_event_id;
char *zErrMsg;
//Error handling omitted for readability
sqlite3_exec(database, "SELECT MAX(id) FROM log_events;", max_id_callback, max_event_id, &zErrMsg)
max_id_callback() function (Currently Empty)
static bool max_id_callback( void *max_id, int count, char **data, char **columns)
{
}
Upvotes: 0
Views: 2136
Reputation: 180286
no matter what I do I can't quite grasp the concept of callback functions
I suppose you understand that "callback function" is a role that a function can have, not a special kind of function. The general idea is that a program calling some module to request services specifies a function that the module should call (a "call back" to the main program) to provide information (via the arguments passed) and / or to request further information (to be supplied via the return value and / or out parameters). The details vary widely, according to the needs of the module and the services it wants to provide.
With sqlite3_exec()
, the callback, if provided, serves to process the rows resulting from the specified query. It is called once for each result row, with one argument that you specify, and other arguments that encode the result column count, result column values, and result column names. Since your particular query should produce exactly one result row, the callback will be called once.
I'm inclined to guess that your main confusion centers around the callback's first argument and sqlite3_exec()
's fourth. Not all callback interfaces feature this kind of user data argument, but many do. It provides for the callback to operate on data specified by the main program, about which the module calling it has no particular information. In your case, that provides a mechanism (other than a global variable) by which your callback function can record the result.
First, however, you should pay attention to the warnings emitted by your compiler, and if it is not warning about this call:
sqlite3_exec(database, "SELECT MAX(id) FROM log_events;", max_id_callback, max_event_id, &zErrMsg)
then you should turn up the warning level or get a better one. The fourth parameter to sqlite3_exec
has type void *
, but you are passing an int
. The best thing you can hope for is that the value of the int
is converted to a void *
, which sqlite will later forward to your callback. Such a conversion from int
to void *
requires a cast in standard C, but more importantly, it doesn't help you. You presumably want the callback to modify the max_event_id
variable, and for that, it needs the variable's address, not its value:
sqlite3_exec(database, "SELECT MAX(id) FROM log_events;", max_id_callback,
&max_event_id, &zErrMsg)
Conversion between void *
and any other object pointer type does not require a cast. You could insert one anyway, but I would advise not to do, because that would interfere with the compiler's ability to warn you when you make a mistake such as -- to choose a random example -- trying to pass an integer where a pointer is required.
Having settled that, the callback implementation for this case is not that hard. You can expect one call, reporting on a result row with one column. The value reported for that column will be the number you want, in text form. So, a template for an appropriate callback might be
#include <assert.h>
static bool max_id_callback( void *max_id, int count, char **data, char **columns) {
int *max_id_int = max_id; // convert the void * back to int *
// We expect exactly one column
assert(count == 1); // (optional)
const char *max_as_text = data[0];
// We can ignore the column name
// TODO: convert string to int and store it in *max_id_int ...
// SQLite should continue normally (though there should be no more rows):
return 0;
}
Upvotes: 4