3

I'm using SQLite's sqlite3_exec function with Static CallBack functions to handle some information. I want to print out the resulting information to a file defined in a class that called sqlite3_exec. What I'm talking about looks kinda like this,

class MakingTheCall{

static int CallBack(void *NotUsed, int argc, char **argv, char **azColName)
{
    for(int x = 0; x < argc; x++)
        File << argv[x];
    File<<"\n";
} 

private:
   static ofstream File;
   void call(){
       sqlite3_exec(database, query, callback, 0, &zErrMsg);
   }
};

what I REALLY want is for the static function to use a instance specific FILE. So which ever instance of MakingTheCall makes the call passes it's unique non-static FILE object.

But because CallBack is static and must be (I think) to be a callback it doesn't get access to the this pointer of the class. So my thought is if the function where a friend function then it could get at the this pointer. This is assuming I'm understanding this post correctly.

I feel my line of thinking is flawed. It feels like there might still be ambiguity as to which this the function would be using.

As a Part 2 to this discussion is there an example anywhere of where a static friend method would ever be used? They aren't mutually exclusive modifiers from what I'm reading here on the first comment so when would you use both?

I'm new to SQLite and to CallBacks so chances are I could be missing something here that could make my life easy. Like the void* pointer how do I use that? It feels like the key to what I want to do.

Thanks ahead of time.

Community
  • 1
  • 1
Dan
  • 2,625
  • 7
  • 39
  • 52

3 Answers3

4

Pass the object pointer in the sqlite3_exec() call:

sqlite3_exec(database, query, callback, (void*) this, &zErrMsg);

And you'll get it back in the callback's NotUsed parameter:

static int CallBack(void *NotUsed, int argc, char **argv, char **azColName)
{
    MakingTheCall* obj = (MakingTheCall*) NotUsed;

    // whatever....
}

Strictly speaking, the Callback() function should be an extern "C" free function, not a static member.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • It might be a good idea to rename the parameter, since it is used now. – Mike Seymour Aug 30 '12 at 16:22
  • 1
    +1 I was going to write the same, although the name of the parameter in my version was `NowUsed` – David Rodríguez - dribeas Aug 30 '12 at 16:22
  • Awesome. I was wondering how to use the void*. Now I feel like I need to change the Title of the question doesn't really have anything to do with the actual answer. Any chance you could speak about `static friend` methods being I am super curious about where that would work. – Dan Aug 30 '12 at 16:29
4

If you are compiling with C++11-support, you can use an anonymous function (lambda expression). Example:

class MakingTheCall
{
private:
   ofstream File;
   void call(){
       sqlite3_exec(database, query,
           [&] (void *NotUsed, int argc, char **argv, char **azColName) -> int
           {
               for(int x = 0; x < argc; x++)
               File << argv[x];
               File<<"\n";
           },
           0, &zErrMsg);
   }
};

[&] means, that all variables available in call are captured by reference, including the this-pointer. See here(wikipedia) for details.

DenisG
  • 59
  • 1
  • 5
  • 2
    You can't use a capture group when the lambda is being casted as a function pointer. This is why the "1st argument to callback" parameter was included in `sqlite3_exec()`. I tried adding `std::function` support to sqlite3, but I failed due to too many `void*` conversions and a high level of impatience when changing the library from C to C++. – Andrew Larsson Feb 06 '14 at 06:29
2

Normally, this is acheived by passing a pointer to the object in as part of a closure parameter when registering the callback. In the case of sqlite3_exec the API is as follows:

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

So I'd pass the pointer to the object in as parameter 4. Your callback function is still static but the first thing it should do is cast the first parameter as a pointer to the class of the object you're interested in. You can then call the desired function which would allow you to write to a File which is specific to the instance you've been passed. So your code might lookk a littel like this:

static int CallBack(void *closure, int argc, char **argv, char **azColName)
{
    MakingTheCall* obj = static_cast<MakingTheCall*>(closure);
    obj->WriteToTheFile(argc, argv);
    return 0; // Have to return something.
}

int WriteToTheFile(int argc, char **argv)
{
    for(int x = 0; x < argc; x++)
        File << argv[x];
    File<<"\n";
} 
Component 10
  • 10,247
  • 7
  • 47
  • 64
  • Is there a reason for the `static_cast` instead of a regular `(MakingTheCall*)(Closure);` cast? – Dan Aug 30 '12 at 16:54
  • 1
    @Dan: Mainly because this is C++, not C. Please check out Konrad Rudolphs answer to this question: http://stackoverflow.com/questions/32168/c-cast-syntax-styles – Component 10 Aug 31 '12 at 07:22