Search code examples
c++sqlitecallback

Wrap Sqlite query callback function C++


In sqlite3's C API, the function sqlite3_exec requires a callback function as a parameter, defined by

static int callback(void* data, int argc, char** argv, char** azColName)

I would like to have it work using a wrapper function to execute before the main callback. I.e. pass down my wrapper function to sqlite3_exec() which calls the actual callback.

A practical use case to illustrate what I am trying to achieve would be logging the response. As the code to log the response would (in my use case) not change across varying queries, since it can loop through the number of columns sent, I would like to have the logging defined once (i.e. in the wrapper) rather than re-defining it for all arbitrary amount of future callback functions defined.

This seems like the way to go but I don't know how to implement this.

I have tried creating a wrapper that takes in another argument - the actual callback, but passing this of course doesn't work as the function definition is then different.

Update: Example code that does not compile

#include <sqlite3.h>

#include <iostream>
#include <vector>


class Callback
{
    public:
        Callback( int(*callback)(void* data, int argc, char** argv, char** azColName), void* data )
            : m_callback(callback), m_data(data) {}
        ~Callback(){}

    public:
        int CallbackWrapper(void* data, int argc, char** argv, char** azColName)
        {
            //Do something

            return m_callback(m_data, argc, argv, azColName);
        }

    private:
        int (*m_callback)(void* data, int argc, char** argv, char** azColName);
        void* m_data;
};


class Product_Queries
{
    public:
        Product_Queries() {}
        ~Product_Queries() {}

    public:
        int GetProductIds_Callback(void* data, int argc, char** argv, char** azColName){
            std::vector<int>* ids = (std::vector<int>*)data;
            ids->push_back(std::stoi(argv[0]));

            return 0;
        }
};



int main(int argc, char* argv[])
{
    sqlite3* db;
    sqlite3_open("database.db", &db);

    Product_Queries product_queries;
    std::vector<int> productIds;
    Callback callback(product_queries.GetProductIds_Callback, (void*)&productIds); //Error here

    sqlite3_exec(db, "SELECT id FROM Products", callback.CallbackWrapper, nullptr, nullptr); //Error here


    sqlite3_close(db);

    return 0;
}

Both errors are invalid use of non-static member function


Solution

  • Something along these lines, perhaps:

    struct OriginalCallback {
      int (*callback)(void*,int,char**,char**);
      void* data;
    };
    
    int WrapperCallback(void* data, int argc, char** argv, char** col_names) {
      // Do the work
      OriginalCallback* original = static_cast<OriginalCallback*>(data);
      return original->callback(original->data, argc, argv, col_names);
    }
    
    int WrapSqliteExec(sqlite3* db, const char *sql,
        int (*callback)(void*,int,char**,char**),
        void* data, char **errmsg) {
      OriginalCallback original{callback, data};
      return sqlite3_exec(db, sql, WrapperCallback, &original, errmsg);
    }