简体   繁体   English

在C ++中正确使用sqlite3的回调函数

[英]Proper use of callback function of sqlite3 in C++

I have the following C++ code for testing purposes in conjunction with SQLite3. 我将以下C ++代码与SQLite3结合在一起进行测试。 It's a class called customer with a callback function declared. 这是一个名为customer的类,其中声明了一个回调函数。 This callback function is called whenever sqlite3_exec() returns results (records) from the SQLite database. 每当sqlite3_exec() )从SQLite数据库返回结果(记录)时,都会调用此回调函数。

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. 我对这种构造不满意的是,用于处理结果的源代码位于类外部的回调函数中,而不是由调用sqlite3_exec()的类方法处理的结果。

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. 我可以使用在回调函数完成从SQL查询结果中提取值之后将在类方法中使用的全局变量。 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? 我是否需要忘记回调函数,而深入研究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? 还是我需要去C ++包装器,我想那里没有回调机制,并且结果被传递回了类方法本身?

// customer
#include "Customer\customer.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\n";

  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 <<"\n";
    }
    else
    {
      if(strcmp(columnName, "Age")==0)
      {
        stringstream(argv[i]) >> columnValueShortInt;
        cout << "columnName = " << columnName << "; value = " << columnValueShortInt <<"\n";
      }
      else // strcmp(columnName, "Id")==0)
      {
        stringstream(argv[i]) >> columnValueInt;
        cout << "columnName = " << columnName << "; value = " << columnValueInt <<"\n";
      }
    }
  }
  cout << "end of call back function \n";
  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\n", 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\n", 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\n", 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\n", 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\n", 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

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. 在这种情况下,通常使用的是利用回调的void * (您称为NotUsed )参数- 在安装回调时定义的参数。 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). 对于C ++,通常将参数设置为指向您感兴趣的对象的this指针,并使回调(在c ++源文件中为extern "C"函数)成为类的friend方法(如有必要)。

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)
{
    ...
}

Using sqlite3_exec has the disadvantages that you have to convert some values back from a string to a number, and that it needs to allocate memory for all result records (which can lead to problems when reading large tables). 使用sqlite3_exec的缺点是您必须将某些值从字符串转换回数字,并且需要为所有结果记录分配内存(这可能会导致读取大表时出现问题)。 Furthermore, the callback always is a separate function (even if it's in the same class). 此外,回调始终是一个单独的函数(即使它在同一类中)。

For your example query, using the sqlite3_prepare / sqlite3_step / sqlite3_finalize API would look like this: 对于您的示例查询,使用sqlite3_prepare / sqlite3_step / sqlite3_finalize API如下所示:

void one_customer::readFromDB(sqlite3* db, int id)
{
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(db, "SELECT FirstName, LastName, Age"
                                    " FROM customerTable"
                                    " WHERE Id = ?", -1, &stmt, NULL);
    if (rc != SQLITE_OK)
        throw string(sqlite3_errmsg(db));

    rc = sqlite3_bind_int(stmt, 1, id);    // Using parameters ("?") is not
    if (rc != SQLITE_OK) {                 // really necessary, but recommended
        string errmsg(sqlite3_errmsg(db)); // (especially for strings) to avoid
        sqlite3_finalize(stmt);            // formatting problems and SQL
        throw errmsg;                      // injection attacks.
    }

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
        string errmsg(sqlite3_errmsg(db));
        sqlite3_finalize(stmt);
        throw errmsg;
    }
    if (rc == SQLITE_DONE) {
        sqlite3_finalize(stmt);
        throw string("customer not found");
    }

    this->id         = id;
    this->first_name = string(sqlite3_column_text(stmt, 0));
    this->last_name  = string(sqlite3_column_text(stmt, 1));
    this->age        =        sqlite3_column_int(stmt, 2);

    sqlite3_finalize(stmt);
}

(This code handles errors by just throwing a string with the error message.) (此代码仅通过在错误消息中抛出一个string来处理错误。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM