0

First of all, this is a newbie question. I've built a basic c++ wrapper class for interacting with sqlite3 in the frame of a small project. Here's a portion of it:

data_wrapper.hpp

#ifndef DATAWRAPPER_H
#define DATAWRAPPER_H

class DataWrapper
{
private:
    sqlite3      *db_;
    const char   *db_file_;
    int          rc_;

    bool prepare ( sqlite3_stmt* statement, const char* query );
    bool check_table ( const char* table_name );

public:
    DataWrapper ( const char* db_file );
    sqlite3* get_database();
};

#endif // DATAWRAPPER_H

data_wrapper.cpp

#include <iostream>
#include <cstring>

#include "data_wrapper.hpp"

using namespace std;

DataWrapper::DataWrapper ( const char* db_file ) : db_ ( nullptr )
{
    db_file_ = db_file;
}

sqlite3* DataWrapper::get_database()
{
    return db_;
}

bool DataWrapper::prepare ( sqlite3_stmt* statement, const char* query )
{
    cout << "preparing: " << query << endl;
    rc_ = sqlite3_prepare_v2 ( db_, query, strlen ( query ), &statement, 0 );
    cout << "statement is: " << &statement << endl;

    return rc_ == SQLITE_OK;
}

/**
 * Checks if a given table is present on the database
 *
 * @param  table_name The name of the table to check.
 * @return bool       True if table exists, false otherwise.
 */
bool DataWrapper::check_table ( const char* table_name )
{
    const char* query = "SELECT name FROM sqlite_master WHERE type='table' AND name=?;";
    sqlite3_stmt* stmt = nullptr;

    if ( !prepare ( stmt, query ) ) {
        cout << "can't prepare query" << endl;

        return false;
    }

    cout << "statement now is: " << stmt << endl;

    if ( !bind ( stmt, 1, table_name ) ) {
        return false;
    }

    step ( stmt );

    return rc_ == SQLITE_ROW;
}

Somewhere, I have an init public method that opens a connection and checks for the presence of a given table. An expert would see the problem, which can be summed up by console output:

preparing: SELECT name FROM sqlite_master WHERE type='table' AND name=?;
done preparing
statement is: 0x7fff6bd121d0
statement now is: 0

Not that the use of sqlite3 is part of the problem here (or is it?), but of course, I won't be able to bind anything on a NULL statement:

(21) API called with NULL prepared statement
(21) misuse at line 76880 of [a65a62893c]

You'll see that I'm losing pointer reference after prepare scope ends. I've been looking around stack for similar problems but none adresses my issue correctly. So how can I prevent that, and possibly modernly, being on a learning curve, I might as well get a grip on actual c++11 techniques.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
nadous
  • 113
  • 3
  • `cout << "statement is: " << &statement << endl;` prints the address of the pointer `statement`, not the address of what it points to. You probably want to remove the `&`. – maddin45 May 03 '17 at 08:45
  • Duplicate of http://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value – UKMonkey May 03 '17 at 08:46
  • Sorry, I've really made my best to avoid that... – nadous May 03 '17 at 08:53
  • 1
    Small other detail: you may want to make `db_file_` a `std::string`, otherwise if what i spoints to goes out of scope you'll be in trouble. (Or strcpy yourself). – doctorlove May 03 '17 at 09:38

2 Answers2

2
bool DataWrapper::prepare ( sqlite3_stmt* statement, const char* query )

The above function takes the statement pointer by value, so when you pass a pointer to the pointer like this:

rc_ = sqlite3_prepare_v2 ( db_, query, strlen ( query ), &statement, 0 );

you are only updating the local copy, not the one at the call site.

The simple fix is to take the pointer by reference:

bool DataWrapper::prepare ( sqlite3_stmt*& statement, const char* query )
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
2

prepare signature should be

bool DataWrapper::prepare ( sqlite3_stmt*& statement, const char* query ) 

Notice the added & for the statement. When you pass the pointer parameter, you copy its value (which is an address). Updating this parameter inside the function does not change the pointer in the calling function. Adding & before change the function to receive the parameter by reference which will make any update inside the called function to update the parameter of the calling function.

Liran Funaro
  • 2,750
  • 2
  • 22
  • 33