LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Can someone please explain why I needed to add a second definition of a callback fct? (https://www.linuxquestions.org/questions/programming-9/can-someone-please-explain-why-i-needed-to-add-a-second-definition-of-a-callback-fct-671416/)

PTrenholme 09-21-2008 01:49 PM

Can someone please explain why I needed to add a second definition of a callback fct?
 
I was writing some code for a sqlite3 application and wanted to use this code
PHP Code:

//*****************************************************
//
// Call-back function used by sqlite3_exec()
//
//*****************************************************
static int process_sql// Return 0 on success          
  
sqlite3 *db,          // Data base being processed    
  
int argc,             // Number of row values returned by query
  
char **argv,          // Data values returned                  
  
char **azColName)     // Name of the data columns returned     
{                                                                
  if (
argc 0) return 1;                                        
  if (
partition(argv[0], db)) {                                  
    
// If we get here we had a data base error. Abort.           
    
sqlite3_close(db);                                           
    exit (
1);                                                    
  }                                                              
  
// Partition the root word                                     
  
if (partition(argv[0], db)) {                                  
    
// If we get here we had a data base error. Abort.           
    
sqlite3_close(db);                                           
    exit (
1);                                                    
  }                                                              
  return 
0;                                                      


with this call
Code:

  rc = sqlite3_exec(db, select, process_sql, db, &errmsg);
and I got an error message that I had a pointer type missmatch in argument 3.

When I replaced that code with this
Code:

  int (*callback)();
  callback=process_sql;
  rc = sqlite3_exec(db, select, callback, db, &errmsg);

the application compiled and ran with no problem.

My problem is that, as far as I can see, both calls should have been identical.:scratch:

Is it, perhaps, some optimization changing the "standard" function definition?

Here's the (very simple) compile line I was using:
Code:

gcc comb3.c -lsqlite3 -o comb3

graemef 09-21-2008 04:09 PM

Your argument list doesn't agree with the argument list defined by sqlite3 (the first argument is a void *)
Code:

typedef int (*sqlite_callback)(void*,int,char**, char**);
You should be able to do a type cast
Code:

rc = sqlite3_exec(db, select, (sqlite3_callback) process_sql, db, &errmsg);

PTrenholme 09-22-2008 08:43 AM

Agreed, the first argument is typed as a void pointer to enable one to pass a pointer to anything into the call back function. But the "type mismatch" compile error specifically referred to the third argument.

Note that the compiler did not complain about
Code:

  int (*callback)();
  callback=process_sql;
  rc = sqlite3_exec(db, select, callback, db, &errmsg);

which, I believe, shows that the "type" of process_sql is int *(void*,int,char**,char**), and my C language specification reference book (C a Reference Manual, Harbison and Steele, 1984) agrees.

Note also that the specification of the sqlite3_exec function:
Code:

int sqlite3_exec(
  sqlite3*,                                  /* An open database */
  const char *sql,                          /* SQL to be evaluated */
  int (*callback)(void*,int,char**,char**),  /* Callback function */
  void *,                                    /* 1st argument to callback */
  char **errmsg                              /* Error msg written here */
);

makes no mention of a sqlite3_callback type. Further, the definition of the sqlite3_callback
Code:

typedef int (*sqlite3_callback)(void*,int,char**, char**);
looks to me a match with
Code:

static int process_sql( // Return 0 on success         
  sqlite3 *db,          // Data base being processed   
  int argc,            // Number of row values returned by query
  char **argv,          // Data values returned                 
  char **azColName)    // Name of the data columns returned

except for the static qualification. (Which is there because I was looking at the code in the SQLite in 5 Minutes or Less on the SQLite web site, vis:
Code:

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
  int i;
  for(i=0; i<argc; i++){
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
  }
  printf("\n");
  return 0;

). I don't think that the static should cause the compiler to complain. Anyhow, I removed the static and still get:
Code:

comb4.c: In function ‘main’:
comb4.c:269: warning: passing argument 3 of ‘sqlite3_exec’ from incompatible pointer type

when I compile.

So I'm still puzzled. :scratch:

paulsm4 09-22-2008 12:50 PM

Hi -

The problem (as you verified) is very definitely not the "static". It's just weirdness with C/C++ function pointer syntax.

Here's a link that might help:

http://publications.gbdirect.co.uk/c..._pointers.html

PTrenholme 09-22-2008 03:55 PM

Hum. O.K., I think I've got it after looking at paulsm's reference. (Thanks Paul.)

The function declaration int foo() {...} tells the compiler to generate code to invoke foo when it sees a foo() somewhere in the code. But a pointer to foo has to be declared as int (*foo)() and so my use of an unqualified foo in the sqlite3_callback function was not correct.

The construct suggested by gramef works because it instructs the compile to use a pointer to the function.

Unfortunately that "explanation" seems to accurately convey my still confused state. Can anyone do it any more clearly?

paulsm4 09-22-2008 05:20 PM

Hi, PTrenholme -

You're exactly right.

The syntax *is* confusing - but this statement is absolutely correct:
Quote:

The function declaration int foo() {...} tells the compiler to generate code to invoke foo when it sees a foo() somewhere in the code. But a pointer to foo has to be declared as int (*foo)() and so my use of an unqualified foo in the sqlite3_callback function was not correct.
*Why* is the syntax so weird? Don't know. I imagine it basically has to do with the parenthesis operators (which are overloaded for "grouping" and "function call"), the "*" operator (which is overloaded for "multiplication", "pointer dereferencing" and "pointer declaration") and the relative precedence between these operators. But the basic answer is "it's just weird".

'Hope that helps .. PSM

graemef 09-22-2008 05:36 PM

I notice that you are getting a warning not an error.
What happens if you declare process_sql to exactly match with sqlite3_callback (ie with void*) and then typecast within the function to a sqlite*?

PTrenholme 09-23-2008 12:07 PM

graemef, since the warning was generated by the calling program, not the called one, I tried
Code:

  rc = sqlite3_exec(db, select, (void *) process_sql, db, &errmsg);
which worked like a charm.

Of course your original suggestion:
Code:

rc = sqlite3_exec(db, select, (sqlite3_callback) process_sql, db, &errmsg);
is a better solution since it make the code much more legible.

But, at the point in the compilation where the warning is being generated the types of the arguments of the callback function are irrelevant since all that's being passes is a pointer to a function. Thus changing the type of the "pointer passed as the first argument to the callback function" in the callback function declaration has no effect on the warning message.

By the way, if you're interested in the complete program it's posted here. It's a app to take a string of characters from the standard input and write all possible permutations of all substrings of the input characters (of two characters or more), to the standard output. The OP in that thread was trying to debug an app that was intended to find all words that could be generated by rearranging the letters in an input word using a home-grown linked list program. I was trying to show the OP that reinventing the wheel was not always the best way to go. . .


All times are GMT -5. The time now is 05:16 PM.