2013-01-21 77 views
12

我有以下C++代碼用於與SQLite3一起進行測試。 這是一個叫做customer的類,聲明瞭一個回調函數。只要sqlite3_exec()從SQLite數據庫返回結果(記錄),就會調用此回調函數。在C++中正確使用sqlite3的回調函數

我不喜歡這種結構的是,處理結果的源代碼位於類外部的回調函數中,而不是由調用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 
+0

是真正的最小代碼示例? –

+0

不是我想的,我是這個論壇的新手,下次嘗試最小化代碼,感謝提示。 –

回答

20

通常不會在這種情況下,是什麼一個是利用void *的(你叫NotUsed)回調的參數 - 參數當您安裝回調你定義。對於C++,您通常會將該參數設置爲this指向您感興趣對象的指針,並且您將爲您的類(如果需要)製作回調函數(在C++源文件中爲extern "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) 
{ 
    ... 
} 
+0

這是byond我目前的c + +知識,而不是說經驗,必須在這一個工作。你的意思是,如果我傳遞這個指針,那麼回調函數可以填充類實例(對象)的屬性,然後在sqlite3_exec調用完成後在方法本身內處理值? –

+0

是的。您需要將傳入的'void *'強制轉換爲您的對象指針類型 - 可能類似於'MyClass * object =(MyClass *)NotUsed;'。從這一點上,你可以訪問公開你的對象的一切;如果你想訪問私有的(或受保護的)事物,你需要聲明(在你的類中)'callback()'是一個朋友函數。您可以在Google搜索中輕鬆找到好友功能的示例。 – mah

+0

謝謝,我會盡力實施你的建議, –

16

使用sqlite3_exec有你有一些值從字符串轉換回數字的缺點,而且它需要爲所有的結果記錄分配內存(其讀大表時可能會導致問題)。 此外,回調總是一個單獨的函數(即使它在同一個類中)。

爲了您的示例查詢,使用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); 
} 

(此代碼由剛剛拋出string出現錯誤消息處理錯誤)

+1

非常感謝人們給予這種有用而詳細的解決方案的速度,我對此印象非常深刻。 –