15

SQLite3 と組み合わせてテストする目的で、次の C++ コードがあります。customerコールバック関数を宣言して呼び出すクラスです。このコールバック関数はsqlite3_exec()、SQLite データベースから結果 (レコード) が返されるたびに呼び出されます。

この構造について私が気に入らないのは、結果を処理するソース コードが、呼び出されたクラス メソッドによって処理される結果ではなく、クラス外のコールバック関数にあることsqlite3_exec()です。

コールバック関数が SQL クエリの結果から値を抽出し終わった後に、クラス メソッドで使用されるグローバル変数を使用できます。しかし、複数のレコードがあり、コールバック関数が数回呼び出された場合はどうなるでしょうか。次に、単一の結果しか得られないことを確認しない限り、配列を操作する必要があります。

コールバック関数のことは忘れて、SQLite API のより深い呼び出しに入る必要がありますか?

または、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
4

2 に答える 2

27

この場合に通常行うことは、コールバックのvoid *(これを呼び出すNotUsed) パラメータ (コールバックをインストールするときに定義するパラメータ)を利用することです。C++ の場合、通常はそのパラメーターをthis目的のオブジェクトへのポインターに設定し、 (必要に応じて)コールバック ( extern "C"C++ ソース ファイル内の関数) をクラスへのメソッドにします。friend

これは次のようになります。

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)
{
    ...
}
于 2013-01-21T11:19:02.387 に答える
20

を使用sqlite3_execすると、一部の値を文字列から数値に変換し直す必要があり、すべての結果レコードにメモリを割り当てる必要があるという欠点があります (大きなテーブルを読み取るときに問題が発生する可能性があります)。さらに、コールバックは常に別の関数です (同じクラスにある場合でも)。

サンプル クエリでは、sqlite3_prepare/ sqlite3_step/ sqlite3_finalizeAPI を使用すると次のようになります。

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);
}

(このコードは、エラー メッセージとともに をスローするだけでエラーを処理しstringます。)

于 2013-01-21T12:06:09.440 に答える