Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
661 views
in Technique[技术] by (71.8m points)

sqlite - Proper use of callback function of sqlite3 in C++

I have the following C++ code for testing purposes in conjunction with SQLite3. It's a class called customer with a callback function declared. This callback function is called whenever sqlite3_exec() returns results (records) from the SQLite database.

What I don't like about this construction is that source code to process the results is located in a call back function outside of the class rather than the results being processed by the class method from which sqlite3_exec() is called.

I could use global variables that will be used in the class method after the callback function has finished extracting the values from the SQL query results. But what if there is more than one record and the call back function is called several times. Then I need to work with arrays unless I make sure that I will only have single results.

Do I need to forget about the callback function and go into deeper calls of the SQLite API?

Or do I need to go to a C++ wrapper, I suppose that there is no call back mechanism there and the results being passed back to the class method itself?

// customer
#include "Customercustomer.h"
//## begin module%50E6CCB50119.additionalDeclarations preserve=yes
static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
  int i;
  char* columnName;
  char* columnValueString;
  short int columnValueShortInt = 0;
  int columnValueInt = 0;

  cout << "begin of callback function
";

  for(i=0; i<argc; i++)
  {
    columnName = azColName[i];
    if (strcmp(columnName, "FirstName")==0 || strcmp(columnName, "LastName")==0)
    {
      columnValueString = argv[i];
      cout << "columnName = " << columnName << "; value = " << columnValueString <<"
";
    }
    else
    {
      if(strcmp(columnName, "Age")==0)
      {
        stringstream(argv[i]) >> columnValueShortInt;
        cout << "columnName = " << columnName << "; value = " << columnValueShortInt <<"
";
      }
      else // strcmp(columnName, "Id")==0)
      {
        stringstream(argv[i]) >> columnValueInt;
        cout << "columnName = " << columnName << "; value = " << columnValueInt <<"
";
      }
    }
  }
  cout << "end of call back function 
";
  return 0;
}

//## end module%50E6CCB50119.additionalDeclarations


// Class customer

customer::customer ()
  //## begin customer::customer%50F969EE01E4.hasinit preserve=no
  //## end customer::customer%50F969EE01E4.hasinit
  //## begin customer::customer%50F969EE01E4.initialization preserve=yes
  //## end customer::customer%50F969EE01E4.initialization
{
  //## begin customer::customer%50F969EE01E4.body preserve=yes
  customerId = 0;
  zErrMsg = 0;

  customerDataBaseRc = sqlite3_open("customerdb", &customerDataBase);
  if(customerDataBaseRc)
  {
    fprintf(stderr, "Can't open database %s
", sqlite3_errmsg(customerDataBase));
    sqlite3_close(customerDataBase);
  }

  const char * pSQL[6];
  const char * sqlStatement;

  pSQL[0] = "create table customerTable (Id int, FirstName varchar(30), LastName varchar(30), Age smallint)";

  // execute all the sql statements
  for(int i = 0; i < 1; i++)
  {
    customerDataBaseRc = sqlite3_exec(customerDataBase, pSQL[i], callback, 0, &zErrMsg);

    if( customerDataBaseRc !=SQLITE_OK )
    {
      fprintf(stderr, "SQL error: %s
", zErrMsg);
      sqlite3_free(zErrMsg);
      break; // break the loop if error occur
    }
  }
  //## end customer::customer%50F969EE01E4.body
}


customer::~customer ()
{
  //## begin customer::~customer%50F93279003E.body preserve=yes
  const char *pSQL[6];

  // Remove all data in customerTable
  pSQL[0] = "delete from customerTable";

  // Drop the table from database
  pSQL[1] = "drop table customerTable";

  // execute all the sql statements
  for(int i = 0; i < 2; i++)
  {
    customerDataBaseRc = sqlite3_exec(customerDataBase, pSQL[i], callback, 0, &zErrMsg);
    if( customerDataBaseRc !=SQLITE_OK )
    {
      fprintf(stderr, "SQL error: %s
", zErrMsg);
      sqlite3_free(zErrMsg);
      break; // break the loop if error occur
    }
  }
  cout << "destructor";
  //## end customer::~customer%50F93279003E.body
}



//## Other Operations (implementation)
unsigned int customer::createCustomer (char  iCustomerFirstName[20], char  iCustomerLastName[20], unsigned short iCustomerAge)
{
  //## begin customer::createCustomer%50EBFFA3036B.body preserve=yes
  const char *sqlStatement;

  string result;          // string which will contain the result

  ostringstream convert;   // stream used for the conversion

  convert << "insert into customerTable (Id, FirstName, LastName, Age) values (" << customerId << ", '" << iCustomerFirstName << "', '" << iCustomerLastName << "', " << iCustomerAge << ")";
  result = convert.str(); // set 'Result' to the contents of the stream

  sqlStatement = result.c_str();

  // Execute sql statement
  customerDataBaseRc = sqlite3_exec(customerDataBase, sqlStatement, callback, 0, &zErrMsg);
  // Check for errors
  if(customerDataBaseRc !=SQLITE_OK )
  {
    fprintf(stderr, "SQL error: %s
", zErrMsg);
    sqlite3_free(zErrMsg);
  }

  return customerId++;
  //## end customer::createCustomer%50EBFFA3036B.body
}

char * customer::getCustomer (unsigned int iCustomerId)
{
  //## begin customer::getCustomer%50ED3D700186.body preserve=yes
  const char *sqlStatement;

  char *tmp ="blabla";

  string result;          // string which will contain the result

  ostringstream convert;   // stream used for the conversion

  convert << "select * from customerTable where Id = " << iCustomerId;
  result = convert.str(); // set 'Result' to the contents of the stream

  sqlStatement = result.c_str();

  // Execute the sql statement
  customerDataBaseRc = sqlite3_exec(customerDataBase, sqlStatement, callback, 0, &zErrMsg);
  // Check for errors
  if(customerDataBaseRc !=SQLITE_OK )
  {
    fprintf(stderr, "SQL error: %s
", zErrMsg);
    sqlite3_free(zErrMsg);
  }

  return tmp;
  //## end customer::getCustomer%50ED3D700186.body
}

// Additional Declarations
  //## begin customer%50E6CCB50119.declarations preserve=yes
  //## end customer%50E6CCB50119.declarations

//## begin module%50E6CCB50119.epilog preserve=yes
//## end module%50E6CCB50119.epilog
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

What one typically does in this case is take advantage of the void * (which you call NotUsed) parameter of the callback -- a parameter you define when you install the callback. For C++, you would typically set that parameter to the this pointer to your interested object, and you would make the callback (an extern "C" function in a c++ source file) a friend method to your class (if necessary).

This would look like this:

class customer
{
    ...
public:
    int callback(int argc, char **argv, char **azColName);
};

static int c_callback(void *param, int argc, char **argv, char **azColName)
{
    customer* cust = reinterpret_cast<customer*>(param);
    return cust->callback(argc, argv, azColName);
}

char* customer::getCustomer(int id)
{
    ...
    rc = sqlite3_exec(db, sql, c_callback, this, &errMsg);
    ...
}

int customer::callback(int argc, char **argv, char **azColName)
{
    ...
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...