2013-01-21 12 views
12

Tôi có mã C++ sau cho mục đích thử nghiệm kết hợp với SQLite3. Đó là một lớp được gọi là customer với chức năng gọi lại được khai báo. Hàm gọi lại này được gọi bất cứ khi nào sqlite3_exec() trả về kết quả (bản ghi) từ cơ sở dữ liệu SQLite.Sử dụng đúng chức năng gọi lại của sqlite3 trong C++

Điều tôi không thích về cấu trúc này là mã nguồn để xử lý kết quả nằm trong hàm gọi lại bên ngoài lớp chứ không phải kết quả đang được xử lý theo phương thức lớp mà từ đó sqlite3_exec() được gọi.

Tôi có thể sử dụng các biến toàn cầu sẽ được sử dụng trong phương thức lớp sau khi hàm gọi lại đã hoàn thành việc trích xuất các giá trị từ kết quả truy vấn SQL. Nhưng nếu có nhiều hơn một bản ghi và hàm gọi lại được gọi nhiều lần. Sau đó, tôi cần phải làm việc với mảng, trừ khi tôi chắc chắn rằng tôi sẽ chỉ có kết quả duy nhất.

Tôi có cần phải quên chức năng gọi lại và đi sâu vào các lệnh gọi API SQLite không?

Hoặc tôi có cần phải đi đến trình bao bọc C++ không, tôi cho rằng không có cơ chế gọi lại ở đó và kết quả được chuyển lại cho chính phương thức lớp?

// 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

là nó mẫu mã thực sự tối thiểu? –

+0

Không thực sự tôi giả sử, tôi mới vào diễn đàn này, sẽ cố gắng để giảm thiểu mã lần sau, nhờ gợi ý. –

Trả lời

20

một gì thường làm trong trường hợp này là tận dụng lợi thế của void * (mà bạn gọi NotUsed) tham số của callback - một tham số bạn xác định khi bạn cài đặt gọi lại. Đối với C++, bạn thường đặt tham số đó thành con trỏ this cho đối tượng quan tâm của mình và bạn sẽ thực hiện cuộc gọi lại (một hàm extern "C" trong tệp nguồn C++) phương thức friend cho lớp của bạn (nếu cần).

này sẽ trông như thế này:

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

Điều này đi qua kiến ​​thức C++ hiện tại của tôi, không phải để nói kinh nghiệm, phải làm việc trên cái này. Bạn có nghĩa là, nếu tôi sẽ vượt qua con trỏ này, các cuộc gọi trở lại chức năng sau đó sẽ có thể điền vào các thuộc tính của dụ lớp (đối tượng) và sau đó xử lý các giá trị trong chính phương pháp sau khi cuộc gọi sqlite3_exec đã được hoàn thành? –

+0

Có. Bạn sẽ cần phải đưa 'void *' vào là kiểu con trỏ đối tượng của bạn - có lẽ với một cái gì đó như 'MyClass * object = (MyClass *) NotUsed;'. Từ thời điểm đó bạn có thể truy cập mọi thứ công khai về đối tượng của bạn; nếu bạn muốn truy cập những thứ riêng tư (hoặc được bảo vệ), bạn cần khai báo (trong lớp của bạn) rằng 'callback()' là một hàm người bạn. Bạn có thể dễ dàng tìm thấy các ví dụ về chức năng của bạn bè trong tìm kiếm trên google. – mah

+0

Cảm ơn, tôi sẽ cố gắng thực hiện các đề xuất của bạn, –

16

Sử dụng sqlite3_exec có những nhược điểm mà bạn phải chuyển đổi một số giá trị trở lại từ một chuỗi đến một con số, và rằng nó cần phải phân bổ bộ nhớ cho tất cả hồ sơ kết quả (trong đó có thể dẫn đến các vấn đề khi đọc các bảng lớn). Hơn nữa, gọi lại luôn là một hàm riêng biệt (ngay cả khi nó nằm trong cùng một lớp).

Đối với truy vấn ví dụ của bạn, bằng cách sử dụng API sqlite3_prepare/sqlite3_step/sqlite3_finalize sẽ trông như thế này:

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

(. Mã này xử lý các lỗi bằng cách chỉ ném một string với thông báo lỗi)

+1

Tôi thực sự ấn tượng bởi tốc độ của những người đưa ra các giải pháp hữu ích và chi tiết như vậy, rất được đánh giá cao. –

Các vấn đề liên quan