0
votes

Tableaux de caractères struct corrompus - SQLite C ++

Je suis un peu perplexe par celui-ci. Tout va bien jusqu'à présent avec l'utilisation de Sqlite mais j'essaie maintenant de stocker mes résultats de la requête dans une structure simple. Lorsque je fais cela dans mon rappel, toutes mes données sont superbes dans mon vecteur Sqlitems, mais dès que le rappel sortit, mon vecteur Sqlitems tenant mes lignes de données est soudainement corrompu. Des idées ce qui pourrait causer cela?

// Simple struct to hold column name and row data
struct SQLrow {
    char   * Column;
    char   * Data;
};

// static Vector to hold SQL rows
static std::vector<SQLrow>       SQLItems;

...
// static callback that handles placing query results into structs and into SQLItems vector
// SQLItems column/row data gets corrupted after this function exits
static int countTablesCallback(void *data, int count, char **rows, char **azColName) {
        int i;

        for (i = 0; i < count; i++) {
            SQLrow newItem = { azColName[i] ,rows[i] };
            SQLItems.push_back(newItem);
        }
        *static_cast<std::vector<SQLrow>*>(data) = SQLItems; // Tried this too but throws an exception 
        return 0;
    }


1 commentaires

Si vous souhaitez accéder aux résultats d'une sélection, n'utilisez pas cette interface de rappel avec sqlite3_exec () . Utilisez un Préparation préparée dans une boucle. Beaucoup plus facile à travailler avec.


3 Réponses :


1
votes

Il suffit de regarder le code, il apparaît que les données pointées par des lignes seront invalidées / détruites / modifiées une fois que le rappel retourne. Donc, vous ne pouvez pas conserver ces indications pour une utilisation ultérieure et devront effectuer une copie des données.

Un moyen simple est de changer colonne et de données à partir de char * à std :: chaîne . À défaut, vous devrez faire une sorte de gestion de la mémoire manuelle (allouer de l'espace avec nouveau , puis la supprimer ultérieurement), qui est sujette d'erreur et pas vraiment souhaitable ces jours-ci.


6 commentaires

Vous et @timo sont corrects. Juste hors de curiosité, comment se passera-t-il en utilisant des types de données Char * au lieu de STD :: String? Avez-vous un exemple de cela quelque part que je peux envelopper ma tête?


Ajouter un constructeur à Sqlrow qui prend deux td :: String comme: SQLROW (STD :: String Colonne, STD :: String Data): colonne (colonne), données (données) {}.


Ensuite, utilisez-le en construisant les deux cordes d'abord comme ceci: STD :: String c (azcolname [I]); std :: string d (rangées [i]); , puis pour construire votre sqlrow comme ceci: Sqlrow Row (C, D); Puis poussez-le.


@Antonio j'aime votre suggestion mais je voulais dire comment j'irais éventuellement à faire cela sans utiliser STD :: String Types?


@DRWBNS Si vous ne voulez pas utiliser std :: string et sa gestion automatique de la mémoire, vous devrez attribuer manuellement l'espace pour les données. Quelque chose comme nouveau char [SHLEN (lignes [i]) + 1] , copiez les données dans, rappelez-vous d'appeler Suppr [] Données lorsque vous avez terminé avec elle . Beaucoup de façons simples de gâcher cela et, comme je l'ai dit dans ma réponse, pas conseillé ces jours-ci.


@ 1201ProgrammAlarm a eu! Merci pour l'entrée!



2
votes

Votre vecteur va bien, le static_cast (code> n'a aucun sens là-bas, sauf si DATA code> est réellement utilisé comme paramètre OUT. Votre problème est, très probablement, que sqlrow code> contient le pointeur de caractère et SQLite supprime les chaînes pointées après le retour du rappel. Changer votre classe sur

struct SQLrow {
    std::string Column;
    std::string Data;
};


0 commentaires

0
votes

À mon avis, il y a très peu de cas dans lesquels vous voulez / besoin d'utiliser une chaîne brute en C ++ et que vous n'êtes pas l'un de ceux-ci. Au fait, j'espère que cela vous aidera ou quelqu'un d'autre d'une manière ou d'une autre:

#include <vector>
#include <stdio.h>
#include <string.h>
#include <iostream>


struct SQLrow {
    char* Column;
    char* Data;
};

void your_callback(int count, char **rows, char **azColName) {

    std::vector<SQLrow> rows_list;

    for (int i = 0; i < count; i++) {

        /* Uncomment this if you want 
           your copy of the strings. If you
           use this, don't forget to free the
           memory yourself with delete[] s1 and
           s2.

        size_t s1_len = strlen(rows[i]);
        size_t s2_len = strlen(azColName[i]);

        char* s1 = new char [sizeof(char) * (s1_len + 1)];
        char* s2 = new char [sizeof(char) * (s2_len + 1)];

        memcpy(s1, rows[i], s1_len);
        s1[s1_len] = '\0';

        memcpy(s2, azColName[i], s2_len);
        s2[s2_len] = '\0';

        SQLrow r = { s1, s2 }; */

        SQLrow r = { rows[i], azColName[i] };

        rows_list.push_back(r);
    }

    // test the result
    for (int i = 0; i < count; i++) {
        SQLrow r = rows_list.at(i);

        std::cout << "rows:" << r.Column << " azColName:" << r.Data << std::endl;
    }
}

// this 2 lines are just for simulating the data
// you will get this 'warning: ISO C++ forbids converting a string constant to   char*''
char* rows[] = {"row1", "row2" , "row3" };
char* colName[] = {"name1", "name2", "name3" };

int main()
{
    your_callback(3, rows, colName);

    return 0;
}


0 commentaires