Übergabe von string anstelle von char* an Funktion (Einsatz von SQLite3)



  • Hallo,

    ich habe folgendes Problem.
    Der unten genannte Code funktioniert. Ich erhalte jedoch Warnungen das eine veraltete Konvertierung von Zeichenketten stattfindet (Zeile 11 und 12 in der Main.cpp).
    Also dachte ich mir ich nehme dann einfach statt char* ein string.
    Hat leider nicht geklappt (ganz viele böse Fehlermeldungen) 🙂 .

    Daher meine Frage:
    Wie kriege ich den Compiler dazu sich nicht mehr zu beschweren? Ich mag keine Warnungen 😃

    Meine Main:

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include "sqlite/sqlite3.h"
    #include "database.h"
    
    using namespace std;
    int main()
    {
        Database* db;
        db = new Database ("test.db");
        db->query("INSERT INTO t_user(a_username,a_password,a_salt) VALUES ('Username','Password','Salt');");
        db->close();
    
        return 0;
    }
    

    Mein Header:

    #ifndef DATABASE_H_INCLUDED
    #define DATABASE_H_INCLUDED
    
    #include <string>
    #include <vector>
    #include <string>
    #include <sqlite3.h>
    
    using namespace std;
    
    class Database
    {
    public:
        Database(char* filename);
        ~Database();
    
        bool open(char* filename);
        vector<vector<string> > query(char* query);
        void close();
    
    private:
        sqlite3 *database;
    };
    
    #endif // DATABASE_H_INCLUDED
    

    Meine Implementierung der Klasse Database:

    #include "database.h"
    #include <string>
    #include <iostream>
    
    Database::Database(char* filename)
    {
        database = NULL;
        open(filename);
    }
    
    Database::~Database()
    {
    }
    
    bool Database::open(char* filename)
    {
        if(sqlite3_open(filename, &database) == SQLITE_OK)
            return true;
    
        return false;
    }
    
    vector<vector<string> > Database::query(char* query)
    {
        sqlite3_stmt *statement;
        vector<vector<string> > results;
    
        if(sqlite3_prepare_v2(database, query, -1, &statement, 0) == SQLITE_OK)
        {
            int cols = sqlite3_column_count(statement);
            int result = 0;
            while(true)
            {
                result = sqlite3_step(statement);
    
                if(result == SQLITE_ROW)
                {
                    vector<string> values;
                    for(int col = 0; col < cols; col++)
                    {
                        values.push_back((char*)sqlite3_column_text(statement, col));
                    }
                    results.push_back(values);
                }
                else
                {
                    break;
                }
            }
    
            sqlite3_finalize(statement);
        }
    
        string error = sqlite3_errmsg(database);
        if(error != "not an error") cout << query << " " << error << endl;
    
        return results;
    }
    
    void Database::close()
    {
        sqlite3_close(database);
    }
    


  • Du bekommst (zu Recht!) eine Warnung.
    Das Verändern von Stringliteralen - also das manipulieren der Bytes des Arrays - führt zu undefiniertem Verhalten.

    Wenn du einen Zeiger vom Typ char* hast, dann implizierst du damit dass der Pointee veränderbar ist.
    Sobald du da aber ein String-Literal zuweist, kann es schnell passieren, dass man über den Zeiger die Zeichenkette des Literals verändert.

    Nimm also einfach char const* . std::string ist nicht nötig.

    Database(char const* filename);
        bool open(char const* filename);
        vector<vector<string> > query(char const* query);
    
    // Source entsprechend Anpassen
    

    Ich mag keine Warnungen

    Das ist gut. Warnungen zeigen in 99% der Fälle tatsächliche Fehlerquellen u.ä. an.





  • Das hat mein Problem behoben.
    Ich schau mir das mit den const-correctness nochmal an.
    Danke für die Hilfe!



  • cooky451 schrieb:

    Mit std::string solltest du da keine Warnungen bekommen. btw
    http://www.parashift.com/c++-faq/const-correctness.html
    http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Resource_Acquisition_Is_Initialization

    Wie meinst du das mit std::string?
    Mein Problem ist ja das ich eine Funktion aufrufe welche char const* erwartet.
    Wenn ich Sone richtig verstanden habe, habe ich bisher nur ein char* übergeben.
    Was zu der genannten Gefahr führt.

    Kannst du deine Aussage bitte etwas erweitern?



  • Statt (const) char* sollst du std::string nehmen und zwar sowohl in deiner main als auch in deinen Klassen oder als Parameter. Da kann dir kaum noch was passieren.

    Wenn du (wegen der C-Kompatibilität) Bibliotheken nutzt, die einen const char* erwarten, dafür gibt es std::string::c_str.
    Und mit RAII meint er in etwa folgendes:

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include "sqlite/sqlite3.h"
    #include "database.h"
    
    using namespace std;
    int main()
    {
        Database db("test.db");
    
        db.query("INSERT INTO t_user(a_username,a_password,a_salt) VALUES ('Username','Password','Salt');");
    
    	// db.close(); optional, aber eig nicht nötig wegen Destruktor
    
        return 0;
    }
    
    #ifndef DATABASE_H_INCLUDED
    #define DATABASE_H_INCLUDED
    
    #include <string>
    #include <vector>
    #include <string>
    #include <sqlite3.h>
    
    using namespace std;
    
    class Database
    {
    public:
        Database(std::string const& filename);
        ~Database();
    
        bool open(std::string const& filename);
        vector<vector<string>> query(std::string const& query);
        void close();
    
    private:
        sqlite3 *database;
    };
    
    #endif // DATABASE_H_INCLUDED
    
    #include "database.h"
    #include <string>
    #include <iostream>
    
    Database::Database(std::string const& filename)
     : database(nullptr)
    {
        open(filename);
    }
    
    Database::~Database()
    {
    	// if ( !closed ) muss eig geprüft werden, alsse ich aber der Einfachheit und der Übung wegen weg
    	close();
    }
    
    bool Database::open(std::string const& filename)
    {
        if ( sqlite3_open(filename.c_str(), &database) == SQLITE_OK )
            return true;
    
        return false;
    }
    
    vector<vector<string> > Database::query(std::string const& query)
    {
        sqlite3_stmt *statement;
        vector<vector<string> > results;
    
        if ( sqlite3_prepare_v2(database, query.c_str(), -1, &statement, 0) == SQLITE_OK )
        {
            int cols = sqlite3_column_count(statement);
            int result = 0;
            while ( true )
            {
                result = sqlite3_step(statement);
    
                if(result == SQLITE_ROW)
                {
                    vector<string> values;
                    for(int col = 0; col < cols; col++)
                    {
                        values.push_back((char*)sqlite3_column_text(statement, col)); // hier sollte was geändert an dem cast
                    }
                    results.push_back(values);
                }
                else
                {
                    break;
                }
            }
    
            sqlite3_finalize(statement);
        }
    
        string error = sqlite3_errmsg(database);
    
        if ( error != "not an error" )
    		cout << query << " " << error << endl;
    
        return results;
    }
    
    void Database::close()
    {
        sqlite3_close(database);
    }
    

    Das ist jedoch ungetestet von mir, ich kenne sqlite nicht und wollte jetzt auch nicht die API nachschlagen. Aber etwa solche Prinzipen sind modern.



  • Danke.

    P.S.
    Asche auf mein Haupt, teilweise wird meine Frage auch durch die FAQ beantwortet. Aber nur teilweise 🙂

    Danke nochmal für die Hilfe.


Anmelden zum Antworten