Преобразовать void* в vector - SQLite Callback
Возникла проблема в извлечении данных из SELECT запроса в SQLite.
Метод для выполнения запроса:
template < typename Result, typename Errmsg >
bool SQLite::Select (std::string sql, Errmsg &errmsg, Result &result){
if (this->errn != SQLITE_OK) {
return false;
}
int rc = sqlite3_exec(this->db, sql.c_str(), this->callback, (void*) &result, NULL);
if (rc != SQLITE_OK) {
return false;
}
return true;
}
Статическая Callback функция для записи этого результата в static vector:
int SQLite::callback (void* &res, int num, char** data, char** col) {
std::map<const char *, std::string> buffer;
std::string key;
std::string val;
for (int i = 0; i < num; i++) {
key = col[i];
val = data[i] ? data[i] : "NULL";
buffer[key.c_str()] = val;
}
reinterpret_cast<std::vector <std::map<const char *, std::string>>> (res).push_back(buffer);
return 0;
}
Может кто подскажет как это решить!?
Для справки! Есть класс Offers, создал статическую переменную Query типа вектор и пробовал записать данные в callback функции (код выше) с помощью такой записи:
::Offers::Query.push_back(buffer);
Но компилятор меня послал за дровами следующей ошибкой:
/usr/bin/ld: /tmp/cc2FAOAz.o: в функции «SQLite::callback(void*, int, char**, char**)»:
sqlite.cpp:(.text+0x1ab): неопределённая ссылка на «Offers::Query[abi:cxx11]»
collect2: error: ld returned 1 exit status
Ответы (2 шт):
Невозможно преобразовать void* к какому то вектору. Можно преобразовать на указатель на вектор:
using Ptrv = std::vector <std::map<const char*, std::string>>*;
reinterpret_cast<Ptrv>(res)->push_back(buffer);
В результате:
using Ptrv = std::vector <std::map<const char*, std::string>>*;
reinterpret_cast<Ptrv>(res)->push_back(buffer);
РАБОТАЕТ! Так же как и более простой код в стиле "C":
std::vector<std::map<std::string, std::string>> *r = (std::vector<std::map<std::string, std::string>>*)res;
Но! Если в buffer и в передаваемой переменной, ключ будет определен как (const char *):
std::map<const char*, std::string> buffer;
В buffer записывается только последние данные в цикле for, а в переменную которая была передана в качестве указателя вообще мусор какой-то. Вот что программа в этом случае выводит:
/*вывод col[i] и data[i] или key и val*/
id = 1
name = Lemberg Premium Горбуша
price_id = 1
distrib_id = 2
url_id = 1
cat_id = 3
manufact_id = 1
/*содержимое buffer*/
Перебор внутри callback:
manufact_id : 1
/*содержимое r (переменная которая передавалась)*/
Перебор внутри Offers:
����%V : 1
Если же вместо (const char*) указать тип std::string то все записывается как надо. А все потому что (const char*) это указатель на переменную col[i] и после завершения callback функции col[i] удаляется из памяти. Соответственно ссылаться не на что.
Были сделаны правки и доведено до рабочего состояния!
Callback функция:
/*static*/ int SQLite::callback (void* res, int num, char** data, char** col) {
std::map<std::string, std::string> buffer;
std::string key;
std::string val;
for (int i = 0; i < num; i++) {
key = col[i];
val = data[i] ? data[i] : "NULL";
buffer[key] = val;
}
std::cout << "Перебор внутри callback:" << std::endl;
for(auto& item : buffer) {
std::cout << item.first << " : " << item.second << std::endl;
}
std::vector<std::map<std::string, std::string>> *r = (std::vector<std::map<std::string, std::string>>*)res;
r->push_back(b);
return 0;
}
Функция в которой вызывается Callback
bool SQLite::Select (std::string sql, char** errmsg, void* result){
if (this->errn != SQLITE_OK) {
return false;
}
int rc = sqlite3_exec(this->db, sql.c_str(), this->callback,result, NULL);
if (rc != SQLITE_OK) {
return false;
}
return true;
}
Пример выполнения запроса
bool Offers::add_offer (GtkWidget *widget) {
std::cout << "Action = add new offer" << std::endl;
char **errmsg;
std::vector<std::map<std::string, std::string>> r;
if (sql.Select("SELECT * FROM offers", errmsg, &r) == false) {
std::cout << "Error request..." << std::endl;
}
else {
std::cout << "Перебор внутри Offers:" << std::endl;
for(auto& item : r[0]) {
std::cout << item.first << " : " << item.second << std::endl;
}
}
return true;
}
Вывод программы в Терминале (в Windows это Консоль)
Перебор внутри callback:
cat_id : 3
distrib_id : 2
id : 1
manufact_id : 1
name : Lemberg Premium Горбуша
price_id : 1
url_id : 1
Перебор внутри Offers:
cat_id : 3
distrib_id : 2
id : 1
manufact_id : 1
name : Lemberg Premium Горбуша
price_id : 1
url_id : 1