I wrote c++ class to connect to mysql database:
hpp file
#include <vector>
#include <string>
#include "mysql/mysql.h"
#ifndef _DATA
#define _DATA
class Database {
public:
string host;
string user;
string pwd;
string db;
MYSQL * connection;
MYSQL_RES *result;
MYSQL_ROW row;
Database(const string & host,
const string & user,
const string & pwd,
const string & db);
int createMysqlConnection();
};
#endif
cpp file
#include "Database.hpp"
Database::Database(const string & host,
const string & user,
const string & pwd,
const string & db) :
mysqlHost(host),
mysqlUser(user),
mysqlPassword(pwd),
mysqlDBName(db)
{}
int Database::createMysqlConnection(){
MYSQL * connection;
connection = mysql_init(NULL);
if(!mysql_real_connect(connection, mysqlHost.c_str(), mysqlUser.c_str(),
mysqlPassword.c_str(), mysqlDBName.c_str(),
0, NULL, 0)){
fprintf(stderr, "Connection to database failed: %s\n",
mysql_error(connection));
return EXIT_FAILURE;
}
cout << "connected to mysql" << endl;
};
When I'm trying to access connection variable from the main function or from a different class I always get an error like variable 'connection is not declared in this scope. I tried to use friend classes or inheritance to point to connection variable but it didn't work. I think I'm doing something wrong in my syntax.
Here is an example of how I try to point to this variable from different class:
Class risk: public Database {
public:
vector<sting> parameter;
Datasabse.connection;
etc....
}
to solve this problem i had to define MYSQL connection in class constructor. here is a code sample:
class1.hpp
***headers***
class Flight {
public:
Flight(MYSQL * connection);
MYSQL * c;
MYSQL_RES * res;
etc...
Class1.cpp
***headers***
Flight::Flight (MYSQL* connection)
{
c = connection;
};
etc...
main.cpp
***headers***
int main (int argc, char *argv[]) {
Database db(localhost, user, etc..);
db.createMysqlConnection;
Flight fl(db.connection);
fl.GetIDs();
etc...
MYSQL * connection;
declares the connection variable as local to the constructor only. You must move it to the class definition in order to make it available somehow. Alternatively, make a connect method in your Database object to explicitely create a new connection adlib. You will have to mirror it with a close method.
Yet another option is to create a dedicated class Connection for connections, which would:
be constructed with a MYSQL *,
be created by the connect method of Database as suggested above; the Database class would act as a Connection factory,
sports all the necessary methods to handle it,
and would call mysql_close in its destructor, to ensure proper termination of the underlying connection.
For instance:
class Connection {
protected:
MYSQL *connection;
public:
Connection(MYSQL *c): connection(c){}
virtual ~Connection() { mysql_close(connection); }
MYSQL_STMT * prepareQuery(const string &query) {
return mysql_prepare(connection, query.c_str(), query.length());
}
// other methods
};
The problem with this approach is that you quickly feel compeled to wrap all the API primitives with classes.
Related
I'm putting objects in a map(code below) when i try to get it the object is empty!
Server s;
s.port = 5400;
commandsMap.insert(std::pair<string,Command>("openDataServer",s));
Server is inherent from Command
class Server: public Command{
public:
static map<string,Varinfo> symbolList;
static map<string,Command> commandsMap;
bool stop = false;
int port;
int execute(vector<string> inputs);
static int lstn(int socketfd,sockaddr_in address);
};
and here is command
class Command{
public:
Command();
int execute(vector<string> inputs);
};
and here I'm trying to find the values that I pushed up there, but the object is from class Command and its empty!
auto it = commandsMap.find(commands[index]);
if ( it != commandsMap.end() ) {
index += it->second.execute(commands);
}
note: commands[index] returns a string
and when I debug after pushing the object I see it right inside the map, but when I use find iterator it->second returns empty object Command
any ideas?
thanks
Update:
I think the problem is that the object that the map finds its not a server object, I want to push in the map many types of classes that inherts Command, and each one to run its own execute() and to have its own fields
the Command that the map returns is returned as Command Class, it should return as Server
final Question:
I want to use a shared pointer as a solution
I have classes like Server that inherits from Command, I want to put them inside a map, and then run thier own execute() as shown upthere
The problem is that you are storing your Server object as a map value of base type Command. This operation slices from Server instance all the fields and leaves only those from base class.
Usual solution to this problem is to store pointer to base class in the map instance of the bare object, and also use virtual interface. So I suggest you to make int execute(vector<string> inputs); virtual and override it in the Server derived class with its specific implementation. In the map use std::unique_ptr<Command> as the value:
https://coliru.stacked-crooked.com/a/df5fa3a97f897977
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <memory>
using namespace std;
struct Varinfo{};
struct sockaddr_in{};
class Command{
public:
Command() {}
virtual int execute(vector<string> inputs) {
std::cout << "From: Command" << std::endl;
return 0;}
};
class Server: public Command{
public:
static map<string,Varinfo> symbolList;
static map<string,Command> commandsMap;
bool stop = false;
int port;
int execute(vector<string> inputs) override {
std::cout << "From: server: port = " << port << std::endl;
return 0;}
static int lstn(int socketfd,sockaddr_in address){return 0;}
};
int main()
{
std::map<std::string, std::unique_ptr<Command>> commandsMap;
auto server_ptr = std::make_unique<Server>();
server_ptr->port = 5400;
commandsMap.emplace("openDataServer", std::move(server_ptr));
// or: commandsMap.emplace("openDataServer", std::make_unique<Server>(...));
int index = 0;
std::vector<std::string> commands = {"openDataServer"};
auto it = commandsMap.find(commands[index]);
if ( it != commandsMap.end() ) {
index += it->second->execute(commands);
}
}
I am using the ODAC components designed by the devart company with embarcadero C++ builder 10.2. Now I want to define an own class, which should be able to establish a connection to an oracle database. So I want to use the TOraSession component inside of my own class but without using the VCL.
The class is defined as written below:
#include "ora.hpp"
#include "Dbaccess.hpp"
class ConnectToDatabase
{
public:
ConnectToDatabase();
virtual ~ConnectToDatabase();
bool getConnected() const {return connected_;};
void establishConnection();
protected:
TOraSession *OraSession;
std::string server_;
std::string username_;
std::string password_;
bool connected_;
std::string port_;
std::string sid_;
std::string servername_;
};
My corresponding cpp-File looks like below:
#pragma link "DBAccess"
#include "connectToDatabase.h"
void ConnectToDatabase::establishConnection()
{
OraSession = new TOraSession(NULL);
OraSession->Options->Direct = True;
OraSession->Server = server_.c_str();
OraSession->Username = username_.c_str();
OraSession->Password = password_.c_str();
if (OraSession->Connected)
{
connected_ = true;
}
else
{
connected_ = false;
}
}
The problem is, that an error occurs while C++ builder tries to link my program.
Error-message:
'DATA.DBCONSTS.OBJ can't be opened'
So I hope, that someone has an idea, what is going wrong.
best regards
Hoeh
I wanna create abstract Db class and implement Pg (postgresql) by inheriting from Db:
Db.h:
template <class T>
class Db
{
public:
Db(std::string, std::string, std::string, std::string);
virtual ~Db();
protected:
T* getConnection();
std::string getHost();
std::string getUsername();
std::string getDatabase();
std::string getPassword();
virtual std::string getConnectionString()=0;
private:
T* connection;
std::string host;
std::string username;
std::string database;
std::string password;
};
Db.cpp:
#include "Db.h"
using namespace std;
template <class T>
Db<T>::Db(string iHost, string iUsername, string iDatabase, string iPassword)
: host(iHost), username(iUsername), database(iDatabase), password(iPassword) {
T* conn(getConnectionString());
connection = conn;
}
template <class T>
T* Db<T>::getConnection() {
return connection;
}
... getters
Pg.h:
#include "../Db.h"
template <class T>
class Pg : public Db<T> {
public:
virtual std::string getConnectionString();
};
Pg.cpp:
#include "Pg.h"
template <class T>
std::string Pg<T>::getConnectionString() {
return "host=" + this->getHost() + " user=" + this->getUsername()
+ " password=" + this->getPassword() + " dbname=" + this->getDatabase();
}
main.cpp:
#include <iostream>
#include <pqxx/connection>
#include <pqxx/transaction>
#include "lib/db/pg/Pg.h"
using namespace std;
int main () {
string host = "localhost";
string username = "username";
string database = "db";
string password = "root";
try {
Pg<pqxx::connection> *db(host, username, database, password);
} catch (pqxx::broken_connection) {
cout << "Failed to establish connection." << endl;
}
return 0;
}
Sorry for long implementation. After compiling with g++ I get an error:
main.cpp: In function ‘int main()’:
main.cpp:15:62: error: expression list treated as compound expression in initializer [-fpermissive]
Pg<pqxx::connection> *db(host, username, database, password);
^
main.cpp:15:62: error: cannot convert ‘std::string {aka std::basic_string<char>}’ to ‘Pg<pqxx::basic_connection<pqxx::connect_direct> >*’ in initialization
I don't know if it is a good idea in the end, because my first version with only main() function looked more compact. I just try to hide connection details somewhere, such as connection string.
In main(), your db variable is declared as a pointer. You cannot pass constructor parameters to a pointer like you are doing.
You need to either:
drop the * from the declaration:
Pg<pqxx::connection> db(host, username, database, password);
keep the * and use new to construct the object:
Pg<pqxx::connection> *db = new Pg<pqxx::connection>(host, username, database, password);
...
delete db;
I'm using Mysql 5.6 with Qt5 and creating a class to encapsulate a database connection.
When I try to create an object in the main with dbConnect conn = new dbConnect("localhost", "test1","test","user"); I get and error telling me I need a conversion from dbConnect* to a non-scalar type.
Reading other posts I've seen this could be due to the type of inicialization I'm trying because it's not a good practice in C++ and should be something like dbConnect conn("localhost", "test1","test","user"); but, changing it like that, instead of getting the conversion error I get now a no matching function for call to QSqlDatabase::setHosTname(std::string&) and same with the other methods needed for connecting such as setDatabaseName, setUserName and setPassword
Could it be because of the std::string I'm using?
Or how should I create the object?
This is m y header db.h:
#ifndef DB
#define DB
#include <QSqlDatabase>
#include <string>
class dbConnect:QSqlDatabase
{
public:
dbConnect(std::string host, std::string name,std::string user, std::string pass);
bool createConnection(std::string host, std::string name,std::string user, std::string pass);
private:
QSqlDatabase dbObject;
};
#endif // DB_
Next here it is its implementation db.cpp:
#include<QSqlDatabase>
#include <QMessageBox>
#include <QSqlError>
class dbConnect:QSqlDatabase
{
private:
QSqlDatabase dbObject;
public:
dbConnect(std::string host, std::string name,std::string user, std::string pass)
{
if(createConnection(host, name, user, pass))
{
QMessageBox::information(0,"Connection Status","Connection established with database");
}
else
{
QMessageBox::critical(0,QObject::tr("Error connecting to database"),dbObject.lastError().text());
}
}
bool createConnection(std::string host, std::string name,std::string user, std::string pass)
{
dbObject = QSqlDatabase::addDatabase("QMYSQL");
dbObject.setHostName(host);
dbObject.setDatabaseName(name);
dbObject.setUserName(user);
dbObject.setPassword(pass);
if(!dbObject.open())
{
return false;
}
else
{
return true;
}
}
};
UPDATE
Took #gengisdave and #R Sahu solutions and now I'm trying to create an object in the main. If I try dbConnect conn(); it works fine even if the constructor takes for paremeter but, if I trye dbConnect conn("localhost","test1","test","user"); compiler gives me an error of undefined reference to dbConnect::dbConnect(std::string,std::string,std::string,std::string).
When you use:
dbConnect conn = new dbConnect("localhost", "test1","test","user");
the RHS is of type dbConnect* while the LHS is of type dbConnect. That is not correct. The compiler cannot take a pointer and assign it to an object.
You can use:
dbConnect* connPtr = new dbConnect("localhost", "test1","test","user");
or
dbConnect conn("localhost", "test1","test","user");
Other problems
bool createConnection(std::string host, std::string name,std::string user, std::string pass)
{
dbObject = QSqlDatabase::addDatabase("QMYSQL");
dbObject.setHostName(host);
dbObject.setDatabaseName(name);
dbObject.setUserName(user);
dbObject.setPassword(pass);
if(!dbObject.open())
{
return false;
}
else
{
return true;
}
}
is not right. You probably need:
bool createConnection(std::string host, std::string name,std::string user, std::string pass)
{
// Since this class is derived from QSqlDatabase,
// you can use:
this->addDatabase("QMYSQL");
this->setHostName(QString::fromStdString(host));
this->setDatabaseName(QString::fromStdString(name));
this->setUserName(QString::fromStdString(user));
this->setPassword(QString::fromStdString(pass));
if(!this->open())
{
return false;
}
else
{
return true;
}
}
Also, remove the member variable
QSqlDatabase dbObject;
You don't need it since the class is already derived from QSqlDatabase. You'll need to use that only if you don't derive from QSqlDatabase
QSQlDatabase::setHostName requires a parameter of type QString, while you provide a std::string; the same with the next 3 lines
you can change that lines to
this->setHostName(QString::fromStdString(host));
this->setDatabaseName(QString::fromStdString(name));
this->setUserName(QString::fromStdString(user));
this->setPassword(QString::fromStdString(pass));
EDIT: it works fine
main.cpp
#include <QApplication>
#include "db.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
dbConnect connection("localhost", "test1", "test", "user");
dbConnect *conn = new dbConnect("localhost", "test1", "test", "user");
}
db.cpp
#include <QSqlDatabase>
#include <QMessageBox>
#include <QSqlError>
#include "db.h"
dbConnect::dbConnect(std::string host, std::string name,std::string user, std::string pass)
{
if ( createConnection(host, name, user, pass) )
QMessageBox::information(0, "Connection Status", "Connection established with database");
else
QMessageBox::critical(0,QObject::tr("Error connecting to database"),dbObject.lastError().text());
}
bool dbConnect::createConnection(std::string host, std::string name,std::string user, std::string pass)
{
this->dbObject = QSqlDatabase::addDatabase("QMYSQL");
this->dbObject.setHostName(QString("localhost"));
this->dbObject.setDatabaseName(QString("test.txt"));
this->dbObject.setUserName(QString("test"));
this->dbObject.setPassword(QString("test"));
return this->dbObject.open();
}
db.h
#ifndef DB_H
#define DB_H
#include <QSqlDatabase>
#include <string>
class dbConnect:QSqlDatabase
{
public:
dbConnect(std::string host, std::string name,std::string user, std::string pass);
bool createConnection(std::string host, std::string name,std::string user, std::string pass);
private:
QSqlDatabase dbObject;
};
#endif
compiled with: gcc db.cpp main.cpp -I/usr/include/qt5/QtSql -I/usr/include/qt5/QtWidgets -I/usr/include/qt5 -fPIC -o main -lstdc++ -lQt5Sql -lQt5Core -lQt5Widgets
In my application I got many instances of class CDbaOciNotifier. They all share a pointer to only one instance of class OCIEnv.
What I like to achieve is that allocation and deallocation of the resource class OCIEnv will be handled automatically inside class CDbaOciNotifier.
The desired behaviour is, with the first instance of class CDbaOciNotifier the environment will be created, after that all following notifiers use that same environment. With the destruction of the last notifier, the environment will be destroyed too (call to custom deleter).
Later on, this cycle can start again with the creation of a new environment.
What I've got so far (using a static factory method to create notifiers):
#pragma once
#include <string>
#include <memory>
#include "boost\noncopyable.hpp"
class CDbaOciNotifier : private boost::noncopyable
{
public:
virtual ~CDbaOciNotifier(void);
static std::auto_ptr<CDbaOciNotifier> createNotifier(const std::string &tnsName, const std::string &user, const std::string &password);
private:
CDbaOciNotifier(OCIEnv* envhp);
// All notifiers share one environment
static OCIEnv* m_ENVHP;
// Custom deleter
static void freeEnvironment(OCIEnv *env);
OCIEnv* m_envhp;
};
CPP:
#include "DbaOciNotifier.h"
using namespace std;
OCIEnv* CDbaOciNotifier::m_ENVHP = 0;
CDbaOciNotifier::~CDbaOciNotifier(void)
{
}
CDbaOciNotifier::CDbaOciNotifier(OCIEnv* envhp)
:m_envhp(envhp)
{
}
void CDbaOciNotifier::freeEnvironment(OCIEnv *env)
{
OCIHandleFree((dvoid *) env, (ub4) OCI_HTYPE_ENV);
*env = null;
}
auto_ptr<CDbaOciNotifier> CDbaOciNotifier::createNotifier(const string &tnsName, const string &user, const string &password)
{
if(!m_ENVHP)
{
OCIEnvCreate( (OCIEnv **) &m_ENVHP, OCI_EVENTS|OCI_OBJECT, (dvoid *)0,
(dvoid * (*)(dvoid *, size_t)) 0,
(dvoid * (*)(dvoid *, dvoid *, size_t))0,
(void (*)(dvoid *, dvoid *)) 0,
(size_t) 0, (dvoid **) 0 );
}
//shared_ptr<OCIEnv> spEnvhp(m_ENVHP, freeEnvironment); ...got so far...
return auto_ptr<CDbaOciNotifier>(new CDbaOciNotifier(m_ENVHP));
}
I'd like to avoid counting references (notifiers) myself, and use something like shared_ptr.
Do you see an easy solution to my problem?
There is a lot going on in your code. Here is the solution, but simplified to just the bare essentials.
class CDbaOciNotifier
{
public:
CDbaOciNotifier() :
m_resource(get_env())
{ }
private:
shared_ptr<OCIEnv> m_env;
struct Delete_env
{
void operator()(OCIEnv* env)
{
OCIHandleFree( ... );
}
};
static shared_ptr<OCIEnv> get_env()
{
// make sure a mutex is involved if CDbaOciNotifier
// can be constructed concurrently.
static weak_ptr<OCIEnv> s_env;
shared_ptr<OCIEnv> env = s_env.lock();
if( ! env )
{
OCIEnv* env_ptr = OCIEnvCreate( ... );
env.reset( env_ptr, Delete_env() );
s_env = env;
}
return env;
}
};
As written you cannot construct CDbaOciNotifier concurrently. You'll need a static mutex to protect s_env if you want that ability.
The weak_ptr needs to be a function local static otherwise you're app might explode if a global or static CDbaOciNotifier is created (static initialization order is undefined).
Does this work for you?
// In .h file
class CDbaOciNotifier
{
// ...
private:
static shared_ptr<OCIEnv> envPtr;
};
// In .cpp file
// Write freeEnvironment as a free function.
shared_ptr<OCIEnv> CDbaOciNotifier::envPtr(new OCIEnv, freeEnvironment);