2013-01-21 11 views
12

Mam następujący kod C++ do celów testowych w połączeniu z SQLite3. Jest to klasa o nazwie customer z zadeklarowaną funkcją wywołania zwrotnego. Ta funkcja wywołania zwrotnego jest wywoływana, gdy tylko sqlite3_exec() zwraca wyniki (rekordy) z bazy danych SQLite.Właściwe wykorzystanie funkcji wywołania zwrotnego sqlite3 w C++

To, co nie podoba mi się w tej konstrukcji, to to, że kod źródłowy do przetworzenia wyniki znajdują się w funkcji oddzwaniania poza klasą, a nie w wynikach przetwarzanych przez metodę klasy, od której jest wywoływana sqlite3_exec().

Mogę użyć zmiennych globalnych, które będą używane w metodzie klasy po zakończeniu funkcji wywołania zwrotnego wyodrębniania wartości z wyników zapytania SQL. Ale co, jeśli jest więcej niż jeden rekord, a funkcja oddzwaniania jest wywoływana kilka razy. Następnie muszę pracować z tablicami, chyba że upewnię się, że będę miał tylko pojedyncze wyniki.

Czy muszę zapomnieć o funkcji wywołania zwrotnego i przejść do głębszych wywołań interfejsu API SQLite?

Czy muszę przejść do opakowania C++, przypuszczam, że nie ma tam mechanizmu oddzwaniania, a wyniki są przekazywane z powrotem do samej metody klasy?

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

jest to naprawdę minimalna próbka kodu? –

+0

Nie do końca przypuszczam, jestem nowy na tym forum, spróbuję zminimalizować kod w kolejnych czasach, dzięki za podpowiedź. –

Odpowiedz

20

Co jeden zwykle nie jest w tym przypadku skorzystać z void * (które nazywamy NotUsed) parametr zwrotnego - parametr zdefiniować podczas instalacji zwrotnego. W przypadku języka C++ typowo ustawiono ten parametr na wskaźnik interesujący dla obiektu this, a wywołanie wywołania zwrotnego (funkcja extern "C" w pliku źródłowym C++) z użyciem metody friend do klasy (w razie potrzeby).

to będzie wyglądać następująco:

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

Dzięki temu moja aktualna wiedza w języku C++, nie mówiąc już o doświadczeniu, musi nad tym pracować. Czy masz na myśli, że jeśli przekażę ten wskaźnik, funkcja callback będzie mogła wypełnić atrybuty instancji klasy (obiektu), a następnie przetworzyć wartości w samej metodzie po zakończeniu wywołania sqlite3_exec? –

+0

Tak. Będziesz musiał rzucić przychodzące 'void *', aby być typem wskaźnika obiektu - być może z czymś takim jak 'MyClass * object = (MyClass *) NotUsed;'. Od tego momentu możesz uzyskać dostęp do wszystkich informacji o swoim obiekcie; jeśli chcesz uzyskać dostęp do prywatnych (lub chronionych) rzeczy, musisz zadeklarować (w swojej klasie), że 'callback()' jest funkcją znajomego. Możesz łatwo znaleźć przykłady funkcji znajomych w wyszukiwaniu google. – mah

+0

Dzięki, postaram się zrealizować Twoje sugestie, –

16

Korzystanie sqlite3_exec ma wady, które trzeba konwertować niektóre wartości z powrotem z łańcucha na liczbę, i że musi przydzielić pamięci dla wszystkich rekordów wynikowych (co może powodować problemy podczas czytania dużych tabel). Ponadto wywołanie zwrotne jest zawsze osobną funkcją (nawet jeśli jest w tej samej klasie).

Dla przykładu zapytania, używając sqlite3_prepare/sqlite3_step/sqlite3_finalize API będzie wyglądać następująco:

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

(. Ten kod obsługuje błędy po prostu rzuca string z komunikatem o błędzie)

+1

Jestem pod dużym wrażeniem szybkości ludzi, którzy docenili tak przydatne i szczegółowe rozwiązania. –

Powiązane problemy