I finally fixed my MySQL connection in C++ Qt. However, when I try to bind values, I get the following error:
QSqlError("2036", "QMYSQL3: Unable to bind value", "Using unsupported buffer type: 1701052421 (parameter: 1)")
I have these files:
Engine.h:
#ifndef ENGINE_H
#define ENGINE_H
#include "database/mysql.h"
class engine
{
private:
static mysql* _mysql;
public:
static void initialize();
static void destroy();
static mysql get_mysql();
};
#endif // ENGINE_H
Engine.cpp:
#include "engine.h"
#include "entities/member_controller.h"
#include <QDebug>
mysql* engine::_mysql;
void engine::initialize()
{
_mysql = new mysql();
member* mem = member_controller::get_member(1);
qDebug() << "mem name = " << mem->getFirstName() << " " << mem->getSecondName();
delete mem;
}
void engine::destroy()
{
delete _mysql;
}
mysql engine::get_mysql()
{
return *_mysql;
}
mysql.h:
#ifndef MYSQL_H
#define MYSQL_H
#include <QtSql>
#include <QString>
#include "mysql_result.h"
class mysql
{
private:
QSqlDatabase db;
public:
mysql();
~mysql();
mysql_result create_result(QString query);
QSqlError error();
QSqlQuery query_prepare(QString query1)
{
QSqlQuery query(this->db);
query.prepare(query1);
// this->query = query;
return query;
}
};
#endif // MYSQL_H
(query_prepare body temp. in header file just to test)
mysql.cpp
#include "mysql.h"
mysql::mysql()
{
this->db = QSqlDatabase::addDatabase("QMYSQL", "QMYSQL");
this->db.setHostName("localhost");
this->db.setUserName("root");
this->db.setPassword("Eequi4");
this->db.setDatabaseName("test");
this->db.open();
}
mysql::~mysql()
{
}
QSqlError mysql::error()
{
return this->db.lastError();
}
member_controller.h:
#ifndef MEMBER_CONTROLLER_H
#define MEMBER_CONTROLLER_H
#include <QString>
#include "member.h"
class member_controller
{
public:
static member* get_member(unsigned int id);
static member* get_member(QString email);
};
#endif // MEMBER_CONTROLLER_H
member_controller.cpp:
#include "member_controller.h"
#include "database/mysql_result.h"
#include "engine.h"
#include "database/mysql_result.h"
#include <QtSql/QSqlQuery>
member* member_controller::get_member(unsigned int id)
{
QSqlQuery result = engine::get_mysql().query_prepare("SELECT * FROM members WHERE member_id = :mem_id");
result.bindValue(":mem_id", id);
if (result.exec() && result.first())
{
return new member(id, result.value("first_name").toString(), result.value("second_name").toString(), result.value("screen_name").toString(), result.value("email").toString(), result.value("status").toString());
}
else
{
qDebug() << engine::get_mysql().error() << "\n";
qDebug() << result.lastError() << "\n";
}
return new member(0, "", "", "", "", "");
}
I hope this is all the code needed. I tried using questionmark except of :mem_id but no luck either.
I am not C++ or Qt expert and have no possibility to debug your code.
But just because of curiosity I've started to investigate your code and found suspicious line (the 2nd one) in your code:
mysql_result result = engine::get_mysql().create_result("SELECT * FROM members WHERE member_id = ?");
Since I am not expert and you didn't provide any includes I don't know neither what is your engine namespace nor function get_mysql() return type nor create_result return.
So I did some guess: get_mysql() probably return QSqlDatabase object? isn't it?
But that type does not support any create_result method! So I stuck there.
Next thanks to google I've found your another question made a week ago. I would please to include such important information in your post next time, so people can see your classes and functions like:
mysql_result mysql::create_result(QString query)
{
return mysql_result(this->db.exec(query));
}
and
mysql_result::mysql_result(QSqlQuery query)
{
this->query = query;
}
Now I can understand what that 2nd line of your code is trying to do.
The suspicious thing I see here is return mysql_result(this->db.exec(query));. That seems according to the function names that you are trying to execute query and get result from mysql server.
But according to the algorithm I see in your member_controller::get_member it seems to me that you are only on prepare stage but not executing yet. I see that Qt documentation is not clear enough (and again i am not an expert) because: Executes a SQL statement on the database and returns a QSqlQuery object. And technically you can say that you have QSqlQuery as a result and you can expect that this result is absolutely the same as if you do:
//mysql_result mysql::create_result(QString query)
QSqlQuery mysql::query_prepare(QString query)
{
QSqlQuery query(this->db);
query.prepare(query);
this->query = query;
return this->query;
}
But IMHO it isn't. In your case it is already executed, that is why you can't bind any parameter later. So I would suggest you to change your mysql_result mysql::create_result(QString query) or create another function QSqlQuery mysql::query_prepare(QString query) which has more sense to me. and change your first lines of code to:
member* member_controller::get_member(int id)
{
QSqlQuery query = engine::get_mysql().query_prepare("SELECT * FROM members WHERE member_id = ?");
query.addBindValue(value);
if (query.exec() && query.first())
...
...
And very last point I don't understand why are you trying to reinvent the weel? If you already have Qt mysql classes which looks very good to me why do you create your own class and methods with one line to call Qt method like:
void mysql_result::add_parameter(QVariant value)
{
this->queryObject.addBindValue(value);
}
Sorry but IMHO that is not very good idea and has almost no sense.
UPDATE Looking into your updated code I see:
result.bindValue(":mem_id", id);
where result type is QSqlQuery so you are calling to the native method bindValue where second parameter according to documentation is const QVariant & val. So I would change your call to:
result.bindValue(":mem_id", QVariant(id));
or
QVariant mem_id(id);
result.bindValue(":mem_id", mem_id);
Related
I have the following JSON-file :
{
"users":[
{"nom":"123",
"name":"John",
"family":"ala",
"cash":1000
}
,{"nom":"456",
"name":"Joe",
"family":"ala",
"cash":1000
}
,{"nom":"131",
"name":"David",
"family":"ala",
"cash":1000
}]
}
I would like to change John's cash.
This is how I am trying to achieve this:
QFile f("file address ...");
f.open(QIODevice::ReadOnly|QIODevice::Text|QIODevice::WriteOnly);
QByteArray b=f.readAll();
QJsonDocument d=QJsonDocument::fromJson(b);
QJsonObject o=d.object();
for (int i=0;i<o["users"].toArray().size();i++) {
if(o["users"].toArray()[i].toObject()["name"].toString()=="John")
o["users"].toArray()[i].toObject()["cash"].toInt()=2000;//error unable to assign
}
However, I am getting the following error:
error: unable to assign
How to fix this?
Cause
You get the error, because you are trying to assign a value to the return value of a function (QJsonValue::toInt in this case).
Solution
Assign the value to QJsonValue, as demonstrated in the JSON Save Game Example:
void Character::write(QJsonObject &json) const
{
json["name"] = mName;
json["level"] = mLevel;
json["classType"] = mClassType;
}
Example
Here is an example I have written for you, in order to demonstrate how your code could be changed to implement the proposed solution:
#include <QApplication>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFile file("data.json");
if (file.open(QFile::ReadOnly | QFile::Text)) {
QJsonObject json = QJsonDocument::fromJson(file.readAll()).object();
QJsonArray users = json["users"].toArray();
file.close();
for (auto user : users) {
QJsonObject userObj = user.toObject();
if (userObj["name"].toString() == "John")
userObj["cash"] = 2000;
user = userObj;
}
qDebug() << users;
}
return a.exec();
}
Result
The given example produces the following result:
QJsonArray([{"cash":2000,"family":"ala","name":"John","nom":"123"},{"cash":1000,"family":"ala","name":"Joe","nom":"456"},{"cash":1000,"family":"ala","name":"David","nom":"131"}])
Please note, that the cash for John is set to 2000.
Sorry if this is a trivial question but I have been trying to build a small .ui that used QSQLITE as database and that uses QTableView to show 4 columns on a default database file as example.
I debugged the problem in every side, changed logical operations of the SQL and restructured the constructor in a more simple way but the error still stays.
After finishing to set up all parameters I am getting this error:
QSqlDatabasePrivate::removeDatabase: connection 'qt_sql_default_connection' is still in use, all queries will cease to work.
And
QSqlDatabasePrivate::addDatabase: duplicate connection name 'qt_sql_default_connection', old connection removed.
I looked on several sources that describe this error such as this source, this other source. Also this was useful but still nothing happens. Official documentation suggests a "wrong" and "right" way to do that here. but the error still stays. After all these different options I brought back the code to a more concise way and I hope that someone can shed light on this issue.
Below the snipped of code:
mainwindow.h
private:
QString temporaryFolder;
dataInfo *mNewDatabaseImages;
QSqlTableModel *mNewTableImages;
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
temporaryFolder = "/home/to/Desktop/tempDBFolder/tmp.db";
QFile dbRem(temporaryFolder);
dbRem.remove();
mNewDatabaseImages = new dataInfo(this);
mNewDatabaseImages->initDataBase(temporaryFolder);
mNewDatabaseImages->confDataBase();
mNewTableImages = new QSqlTableModel(this, mNewDatabaseImages->getDatabase());
mNewTableImages->setTable("leftCamTable");
mNewTableImages->select();
ui->bookMarkTableView->setModel(mNewTableImages);
ui->bookMarkTableView->showColumn(true);
}
datainfo.h
#ifndef DATAINFO_H
#define DATAINFO_H
#include <QObject>
#include <QSqlDatabase>
#include "imageparam.h"
class dataInfo : public QObject
{
Q_OBJECT
public:
explicit dataInfo(QObject *parent = nullptr);
bool initDataBase(const QString &nameDB);
bool confDataBase();
bool addItem(ImageParam* imageItem);
QSqlDatabase getDatabase();
private:
QString mError;
QSqlDatabase mDBImages;
};
#endif // DATAINFO_H
datainfo.cpp
#include "datainfo.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QVariant>
#include <QMessageBox>
#define CREATE_TABLE \
" CREATE TABLE IF NOT EXISTS imageTable" \
" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL" \
" path1 TEXT NOT NULL" \
" path2 TEXT NOT NULL" \
" imageA BLOB NOT NULL" \
" imageB BLOB NOT NULL)"
dataInfo::dataInfo(QObject *parent) : QObject(parent)
{}
bool dataInfo::initDataBase(const QString &nameDB)
{
mDBImages = QSqlDatabase::addDatabase("QSQLITE");
mDBImages.setDatabaseName(nameDB);
bool ok = mDBImages.open();
if(!ok) {
mError = mDBImages.lastError().text();
qDebug() << mError;
}
return ok;
}
bool dataInfo::confDataBase()
{
QSqlQuery qry;
bool ok = qry.exec(CREATE_TABLE);
if(!ok) {
mError = qry.lastError().text();
}
return ok;
}
bool dataInfo::addItem(ImageParam *imageItem)
{
QSqlQuery qry;
qry.prepare("INSERT INTO imageTable (path1, path2, imageA, imageB)" \
" VALUES (?,?,?,?)");
qry.addBindValue(imageItem->path1());
qry.addBindValue(imageItem->path2());
qry.addBindValue(imageItem->image1());
qry.addBindValue(imageItem->image2());
bool ok = qry.exec();
if(!ok) {
mError = qry.lastError().text();
}
return ok;
}
QSqlDatabase dataInfo::getDatabase()
{
return mDBImages;
}
I looked also at this post that suggested to set a name to the databse first but I already do that in the function initDataBase(const QString &nameDB). Here the post that suggested the procedure, which is below:
db->setDatabaseName("name");
if(!db->open()) {
qDebug() << "Error opening ";
return false;
}
Please shed light on the possible solution.
Short answer
You should specify the database you want to run your QSqlQuery on, otherwise they will run on the default database. You can specify the database in QSqlQuery's constructor with
QSqlQuery query(QSqlDatabase::database("my-db"));
You are keeping a copy of the QSqlDatabase as a member of your dataInfo class, which will prevent it prevent closing properly. Instead, just use the static QSqlDatabase::database("name") when needed.
auto db = QSqlDatabase::database("my-db");
Details
Providing the right database to QSqlQuery
Change all your uses of QSqlQuery. For example for confDataBase:
bool dataInfo::confDataBase()
{
// Explicitly provide your database to the query
// Otherwise the default database is used
QSqlQuery qry(getDatabase());
bool ok = qry.exec(CREATE_TABLE);
if(!ok) {
mError = qry.lastError().text();
}
return ok;
}
Not keeping QSqlDatabase attributes
From the documentation:
Warning: It is highly recommended that you do not keep a copy of the QSqlDatabase around as a member of a class, as this will prevent the instance from being correctly cleaned up on shutdown. If you need to access an existing QSqlDatabase, it should be accessed with database(). If you chose to have a QSqlDatabase member variable, this needs to be deleted before the QCoreApplication instance is deleted, otherwise it may lead to undefined behavior.
Store your database's name in your class instead, and change your getDatabase to
dataInfo.cpp
bool dataInfo::initDataBase(const QString &nameDB)
{
// Save database's name
mDBName = nameDB;
// Use the database locally, without storing it
auto dbImages = QSqlDatabase::addDatabase("QSQLITE", nameDB);
bool ok = dbImages.open();
if(!ok) {
mError = dbImages.lastError().text();
qDebug() << mError;
}
return ok;
}
QSqlDatabase dataInfo::getDatabase()
{
return QSqlDatabase::database(mDBName);
}
dataInfo.h
private:
QString mError;
QString mDBName;
Qt's code generating the warning
To have a look at the actual code producing the error: https://code.woboq.org/qt5/qtbase/src/sql/kernel/qsqldatabase.cpp.html#170
invalidateDb is used when connections are added or removed, and will trigger the error if the reference count > 1. As you are holding onto one, this will trigger the error.
I'm currently working on applciation that will add to table view new row, that was inserted into db's table. I started with basic class to handle notifies and setted up triggers:
CREATE OR REPLACE FUNCTION notify_tableIWantToObserve_update()
RETURNS trigger AS $$
DECLARE
BEGIN
PERFORM pg_notify(
CAST('tableIWantToObserve_update' AS text),
(NEW.tableIWantToObserve_id)::text);
return new;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER tRIGGER_notify_tableIWantToObserve_update
AFTER UPDATE
ON tableIWantToObserve
FOR EACH ROW
EXECUTE PROCEDURE notify_tableIWantToObserve_update();
So it will jsut send notfy with id of updated row in payload. That is what i want - becous reloading whole table just won't do the trick later.
I checked documentaton of QSqlDriver
http://doc.qt.io/qt-5/qsqldriver.html#notification-1
With it, I created my "handler":
// That's its constructor
MyDB = new QSqlDatabase(QSqlDatabase::addDatabase("QPSQL", "Main"));
//Removed my data from here (just fro sake of this post)
MyDB->setHostName("-");
MyDB->setPort(0);
MyDB->setDatabaseName("-");
MyDB->setUserName("-");
MyDB->setPassword("-");
MyDB->open();
if( MyDB->isOpen() )
{
qDebug()<<"Connected to DB!";
QObject::connect(
MyDB->driver(),
SIGNAL(notification(const QString&, QSqlDriver::NotificationSource, const QVariant)),
this,
SLOT(slot_DBNotification_Recieved_NotifiAndPayload((const QString&, const QVariant)));
);
}
else
qDebug()<<"NOT connected to DB!";
But it jsut wont work. Only with driver's signal useing single QString it will connect it - version i needed (with additional info) wont connect.
I updated my QT to 5.7, but still even in QTCreater, it just shows me that driver's signal is only with single string.
Is there any fix for that? I realy need to use that signal to retrieve that updated row id.
EDIT 1:
that slot of my handler:
void NotifiHandlerr::slot_DBNotification_Recieved_NotifiAndPayload(const QString& MSG, const QVariant &payload)
{
qDebug() << "I WAS NOTIFIED ABOUT : " + MSG+" WITH DATA : "+payload.toString();
}
EDIT 2:
I tried to add QSqlDriver::NotificationSource as argument in my slot, but i couldn't - it still repeated error in .h that NotificationSource wasn't declared.
EDIT 3:
I'm adding here most of the code (handler class)
// WHOLE .h
#include <QDebug>
#include <QObject>
#include <QString>
#include <QSqlDatabase>
#include <QSqlDriver>
#include <QVariant>
#include <QSqlDriverPlugin>
#include <qsqldriver.h>
class Handler : public QObject
{
Q_OBJECT
public slots:
void slot_DBNotification_Recieved_NotifiAndPayload
(const QString& name, QSqlDriver::NotificationSource source, const QVariant& payload);
public:
explicit Handler();
~Handler();
private:
QSqlDatabase MyDB;
};
//WHOLE .cpp
#include "Handler.h"
Handler::Handler()
{
MyDB = new QSqlDatabase(QSqlDatabase::addDatabase("QPSQL", "Main"));
MyDB->setHostName("-");
MyDB->setPort(0);
MyDB->setDatabaseName("-");
MyDB->setUserName("-");
MyDB->setPassword("-");
MyDB->open();
if( MyDB->isOpen() )
{
qDebug()<<"Connected to DB!";
MyDB->driver()->subscribeToNotification("tableIWantToObserve_update");
QObject::connect(
MyDB->driver(),
SIGNAL(notification(const QString&, QSqlDriver::NotificationSource, const QVariant)),
this,
SLOT(slot_DBNotification_Recieved_NotifiAndPayload((const QString&, const QVariant)));
);
}
else
qDebug()<<"NOT connected to DB!";
}
Handler::~Handler()
{
MyDB->driver()->unsubscribeFromNotification("tableIWantToObserve_update");
MyDB->cloe();
}
void NotificationMaster::slot_DBNotification_Recieved_NotifiAndPayload
(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload)
{
qDebug() << "I WAS NOTIFIED ABOUT : " + name+" WITH DATA : "+payload.toString();
}
And just to eliminate this idea - I added
QT += sql
in my .pro file
Your slot has a wrong signature, Here is how you should define it.
In your header file:
//in order to be able to use the enum QSqlDriver::NotificationSource
#include <QSqlDriver>
...
...
class Handler : public QObject{
Q_OBJECT
public:
explicit Handler(QObject *parent = 0);
~Handler();
...
...
...
public slots:
void SqlNotification(const QString& name, QSqlDriver::NotificationSource source,
const QVariant& payload);
...
...
};
and in the constructor, when you are connecting the slot, you should subscribe to the notification first:
QSqlDatabase::database().driver()->subscribeToNotification("notification_name");
connect(QSqlDatabase::database().driver(),
SIGNAL(notification(QString,QSqlDriver::NotificationSource,QVariant)), this,
SLOT(SqlNotification(QString,QSqlDriver::NotificationSource,QVariant)));
You may need to unsubscribe in the destructor(since you don't want to receive the notification any more):
QSqlDatabase::database().driver()->unsubscribeFromNotification("notification_name");
and your slot implementation:
void Handler::SqlNotification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload){
switch(source){
case QSqlDriver::UnknownSource:
qDebug() << "unkown source, name: " << name << "payload:" << payload.toString();
break;
case QSqlDriver::SelfSource:
qDebug() << "self source, name: " << name << "payload:" << payload.toString();
break;
case QSqlDriver::OtherSource:
qDebug() << "other source, name: " << name << "payload:" << payload.toString();
break;
}
}
Code posted by me and in anserw were more-or-less correct, but what need to be done is to recreate whoel project in newer version of qt creator so it will resolve issiues with missing functions and namespaces itself. Just create new project and paste all files there.
I'm seeing a strange segmentation fault when accessing a QString type struct member from GET_STATION_MODEL_PARAMS_t. The execution is stopped at **:
code:
#include <QDebug>
#include "hradcontroller.h"
#include "hradAPI.h"
#define TAG "HRAD_CONTROLLER:\0"
#define HRAD_API_VERSION "2"
#define MANUFACTURER_KEY "abcde12345"
typedef struct registerDeviceParams{
QString brand;
QString device;
QString manufacturer;
QString model;
QString serial;
QString ibd;
QString ibkey;
QString version;
QString track;
registerDeviceCB_t cbFn;
}REGISTER_DEVICE_PARAMS_t;
typedef struct getStationModelParams{
int stationID;
QString ibd;
QString ibkey;
QString version;
QString track;
getStationModelCB_t cbFn;
GET_STATION_MODEL_RESPONSE_t* resp;
}GET_STATION_MODEL_PARAMS_t;
HRAD *hbr = new HRAD();
void registerDevice(){
REGISTER_DEVICE_PARAMS_t params;
params.brand = "brand";
params.device = "TI-J6";
params.manufacturer = "manuf";
params.model = "EVK";
params.ibkey = MANUFACTURER_KEY;
params.serial = "abcde12345";
params.version = HRAD_API_VERSION;
params.track = "true";
params.cbFn = deviceRegisteredCB;
hbr->registerDevice(¶ms);
}
void getStationModel(int id){
GET_STATION_MODEL_PARAMS_t params;
params.stationID = id;
params.ibd = hbr->getId();
params.ibkey = "abdef";
params.version = "2";
** params.track = "false"; //SIGSEGV segmantation fault here
params.cbFn = stationModelAvailableCB;
params.resp = &g_StationModel;
}
void deviceRegisteredCB(QString ibd){
qDebug() << TAG << "Device Registered: ibd = " << ibd;
getStationModel(currentPublicStationID);
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
/* Register ourselves to HRAD Server */
registerDevice();
}
Values just before the seg. fault:
If you notice "track" is "not accessible" while debugging and this causes crash.
I modified GET_STATION_MODEL_PARAMS_t like this:
typedef struct getStationModelParams{
int stationID;
QString foo; //dummy variable.
QString ibd;
QString ibkey;
QString version; //seg. fault here now
QString track;
getStationModelCB_t cbFn;
GET_STATION_MODEL_RESPONSE_t* resp;
}GET_STATION_MODEL_PARAMS_t;
And values became like this:
Now "version" is also "not accessible".
Any idea why is this happening?
Thanks.
Update 1:
Tools:
Qt Creator 3.4.1 (ubuntu 14.04)
Qt 5.4.2 (GCC 4.6.1, 64 bit)
TL;DR: This is why you really need to post all the code needed to reproduce the crash.
Your code works - no crashes. Your question is thus answered, even if the answer is completely useless to you, and everyone else too. Don't ask questions that can only be answered in a way that can't help you.
The only thing that stands out in your code, besides it being a horrible C abomination with no place in modern C++, is that the HRAD instance is created before main starts executing. Depending on what HRAD's constructor is doing, this might be a bad thing.
But let's see if we can fix your code up some, to guide you to write safer code.
Old Code
First, let's start with your code verbatim, with some additions at the beginning and end to get it to compile and execute.
You should have provided all that in your question - with enough of "other stuff" to crash. The below, assuming that it crashed or otherwise reproduced your problem, would be considered an sscce and make your question valuable to the community.
#include <QString>
#include <QDebug>
typedef void (*registerDeviceCB_t)(QString);
typedef void (*getStationModelCB_t)();
typedef struct getStationModelResponse {} GET_STATION_MODEL_RESPONSE_t;
void deviceRegisteredCB(QString);
void stationModelAvailableCB() {}
typedef struct registerDeviceParams REGISTER_DEVICE_PARAMS_t;
struct HRAD {
void registerDevice(REGISTER_DEVICE_PARAMS_t*);
QString getId() { return QString(); }
};
GET_STATION_MODEL_RESPONSE_t g_StationModel;
int currentPublicStationID = 1;
void registerDevice();
void getStationModel(int);
int main()
{
registerDevice();
getStationModel(0);
return 0;
}
// Your code verbatim beyond this point
Your code, copy-pasted for reference (with apparently irrelevant MainWindow removed):
#define TAG "HRAD_CONTROLLER:\0"
#define HRAD_API_VERSION "2"
#define MANUFACTURER_KEY "abcde12345"
typedef struct registerDeviceParams{
QString brand;
QString device;
QString manufacturer;
QString model;
QString serial;
QString ibd;
QString ibkey;
QString version;
QString track;
registerDeviceCB_t cbFn;
}REGISTER_DEVICE_PARAMS_t;
typedef struct getStationModelParams{
int stationID;
QString ibd;
QString ibkey;
QString version;
QString track;
getStationModelCB_t cbFn;
GET_STATION_MODEL_RESPONSE_t* resp;
}GET_STATION_MODEL_PARAMS_t;
HRAD *hbr = new HRAD();
void registerDevice(){
REGISTER_DEVICE_PARAMS_t params;
params.brand = "brand";
params.device = "TI-J6";
params.manufacturer = "manuf";
params.model = "EVK";
params.ibkey = MANUFACTURER_KEY;
params.serial = "abcde12345";
params.version = HRAD_API_VERSION;
params.track = "true";
params.cbFn = deviceRegisteredCB;
hbr->registerDevice(¶ms);
}
void getStationModel(int id){
GET_STATION_MODEL_PARAMS_t params;
params.stationID = id;
params.ibd = hbr->getId();
params.ibkey = "abdef";
params.version = "2";
params.track = "false"; //SIGSEGV segmantation fault here
params.cbFn = stationModelAvailableCB;
params.resp = &g_StationModel;
}
void deviceRegisteredCB(QString ibd){
qDebug() << TAG << "Device Registered: ibd = " << ibd;
getStationModel(currentPublicStationID);
}
// End of verbatim code
And, finally, the implementation of registerDevice:
void HRAD::registerDevice(REGISTER_DEVICE_PARAMS_t* params) {
params->cbFn("some ibd");
}
These three pieces, when put into a main.cpp, will compile, run, and properly invoke the callback. To wit:
Starting bad-c-aargh-31542746...
HRAD_CONTROLLER: Device Registered: ibd = "some ibd"
bad-c-aargh-31542746 exited with code 0
Rework
It seems that you're using callbacks and passing values via pointers, as you would in C. We'll get rid of the C typedefs that are unnecessary in C++, and we will leverage C++11's features to make the callbacks much more usable.
As above, the code below is merely interspersed with the commentary. You could copy-paste it to a main.cpp, remove the commentary, compile it and run. It is complete.
First, let's include the functional header to bring in std::function, and we shall get rid of C-style macros:
#include <QString>
#include <QDebug>
#include <functional>
const QString TAG = QStringLiteral("HRAD_CONTROLLER:");
const QString HRAD_API_VERSION = QStringLiteral("2");
const QString MANUFACTURER_KEY = QStringLiteral("abcde12345");
Then we shall define the structures used to pass parameters to HRAD methods, and to obtain the responses from these methods:
struct RegisterDeviceParams {
QString brand;
QString device;
QString manufacturer;
QString model;
QString serial;
QString ibd;
QString ibkey;
QString version;
QString track;
};
struct GetStationModelParams {
int stationID;
QString ibd;
QString ibkey;
QString version;
QString track;
};
struct GetStationModelReponse {};
We can then implement a skeleton HRAD class. The parameters are passed as const references. The optional callbacks can be compatible lambdas, functor instances, function pointers, etc. The provision of default-constructed values makes them optional. You can leave them out from a call, the compiler will use the defaults instead. The currentPublicStationID, presumably a global variable, belongs in HRAD, too.
class HRAD {
public:
void registerDevice(const RegisterDeviceParams &,
const std::function<void(const QString & ibd)> & cb
= std::function<void(const QString&)>()) {
cb("some ibd");
}
void getStationModel(const GetStationModelParams &,
const std::function<void(const GetStationModelReponse &)> & cb
= std::function<void(const GetStationModelReponse&)>()) {
GetStationModelReponse response;
cb(response);
}
QString getId() { return "some id"; }
int currentPublicStationID;
HRAD() : currentPublicStationID(1) {}
};
The global instance of HRAD is defined using the Q_GLOBAL_STATIC macro. It will be instantiated, thread-safely, upon first use. It acts as a pointer - to get the global instance, you should use the -> operator.
Q_GLOBAL_STATIC(HRAD, hbr) // no semicolon needed
The getStationModel and registerDevice functions will use lambda syntax to instantiate the callbacks on the spot. Of course if the callbacks were more complex, you could put them in stand-alone functions or methods.
void getStationModel(int id){
GetStationModelParams params;
params.stationID = id;
params.ibd = hbr->getId();
params.ibkey = "abdef";
params.version = "2";
params.track = "false";
hbr->getStationModel(params, [](const GetStationModelReponse&){
qDebug() << "got station model";
});
}
void registerDevice(){
RegisterDeviceParams params;
params.brand = "brand";
params.device = "TI-J6";
params.manufacturer = "manuf";
params.model = "EVK";
params.ibkey = MANUFACTURER_KEY;
params.serial = "abcde12345";
params.version = HRAD_API_VERSION;
params.track = "true";
hbr->registerDevice(params, [](const QString & ibd){
qDebug() << TAG << "Device Registered: ibd = " << ibd;
getStationModel(hbr->currentPublicStationID);
});
}
Finally, we call registerDevice from main:
int main()
{
registerDevice();
return 0;
}
This code works, and produces the following output:
Starting bad-c-aargh-31542746...
"HRAD_CONTROLLER:" Device Registered: ibd = "some ibd"
got station model
bad-c-aargh-31542746 exited with code 0
Note the absence of any manual memory management. Raw pointers aren't used either - neither as parameters, nor as members.
I am using Blackberry Cascades.
I am trying to access my database to insert/edit etc. my database but the app crashes the moment I try to call on the query function favourite().
The app crashes and nothing gets logged in the console so I am not sure what exactly is causing it.
I have also declared in the header: SqlDataAccess * sda
#include "dbHelper.h"
#include <bb/data/SqlDataAccess>
#include <bb/data/DataAccessError>
#include <QtSql/QtSql>
#include <QDebug>
dbHelper::dbHelper()
{
initDatabase();
SqlDataAccess sda (mdbFilewithPath);
// or should I use: this-> sda = new SqlDataAccess();
}
void dbHelper::initDatabase(){
//Copy database to Data folder ... pretty sure this part of the code works.
mdbFilewithPath = QDir::homePath() + "/data.db";
}
void dbHelper::executeQuery (const QString query){
this->sda->execute(query);
if (this->sda->hasError()){
DataAccessError err = this->sda->error();
qWarning() << "SQL Error: " << err.errorType() <<err.errorMessage();
}
}
void dbHelper::favourite(const QString &rule_id)
{
QVariantMap rule;
rule["id"] = rule_id;
QString query ="INSERT INTO user (rule_id, isFavourite) VALUES (" + rule_id + ", 1)";
executeQuery(query);
}
I am not sure what is causing it? Would appreciate any help thanks!