How to get a whole row from database using SOCI? - c++

... and save it into self-defined object type? I'm using PostgreSQL. When I have everything in one file, it works. But I wanted to split this into class-files like you always do when writing in cpp. When I divided my code into *.h and *.cpp files, I'm getting errors.
Here are my files:
test.h
class MyInt
{
public:
MyInt();
MyInt(int i);
void set(int i);
int get() const;
private:
int i_;
};
test.cpp
#include "test.h"
#include <soci.h>
#include <postgresql/soci-postgresql.h>
MyInt::MyInt()
{
}
MyInt::MyInt(int i)
{
this->i_ = i;
}
int MyInt::get() const
{
return this->i_;
}
void MyInt::set(int i)
{
this->i_ - i;
}
namespace soci
{
template <>
struct type_conversion<MyInt>
{
typedef int base_type;
static void from_base(int i, soci::indicator ind, MyInt & mi)
{
if (ind == soci::i_null)
{
throw soci_error("Null value not allowed for this type");
}
mi.set(i);
}
static void to_base(const MyInt & mi, int & i, soci::indicator & ind)
{
i = mi.get();
ind = soci::i_ok;
}
};
}
main.cpp
#include <iostream>
#include "test.h"
int main(int argc, char **argv)
{
MyInt i;
sql.open(soci::postgresql, "dbname=mydb user=postgres password=postgrespass");
sql << "SELECT count(*) FROM person;", soci::into(i);
std::cout << "We have " << i.get() << " persons in the database.\n";
sql.close();
return 0;
}
I compile it like this:
g++ main_test.cpp test.h test.cpp -o App -lsoci_core -lsoci_postgresql
-ldl -lpq -I /usr/local/include/soci -I /usr/include/postgresql
and got those errors:
In file included from /usr/local/include/soci/into-type.h:13:0,
from /usr/local/include/soci/blob-exchange.h:12,
from /usr/local/include/soci/soci.h:18,
from main_test.cpp:3:
/usr/local/include/soci/exchange-traits.h: In instantiation of â€soci::details::exchange_traits<MyInt>’:
/usr/local/include/soci/into.h:29:60: instantiated from â€soci::details::into_type_ptr soci::into(T&) [with T = MyInt, soci::details::into_type_ptr = soci::details::type_ptr<soci::details::into_type_base>]’
main_test.cpp:29:59: instantiated from here
/usr/local/include/soci/exchange-traits.h:35:5: error: incomplete type â€soci::details::exchange_traits<MyInt>’ used in nested name specifier
THE ABOVE PROBLEM IS SOLVED, TAKE A LOOK AT #JohnBandela ANSWER.

The code where you specialize type_conversion
template<>
struct type_conversion<MyInt>
Needs to be in test.h not test.cpp. The problem is if you have it in test.cpp like you do now, it is not visible in main.cpp where you are using SOCI

Related

C++ derived class constructor call base class constructor errors

I have a base class sprite:
sprite.hh
#include <iostream>
#include "vector.hh"
#ifndef SPRITE
#define SPRITE
class sprite
{
private:
vector pos;
//the width and height of the sprite
vector dimensions;
int imArrIndex;
public:
sprite();
sprite(vector p, vector d, int i);
void setPos(vector v);
vector getPos();
void setDimensions(vector v);
vector getDimensions();
void setImArrIndex(int i);
int getImArrIndex();
void movePos(vector v);
};
#endif
sprite.cc:
#include <iostream>
#include "sprite.hh"
#include "vector.hh"
using namespace std;
sprite::sprite()
{
cout << "sprite created\n";
}
sprite::sprite(vector p, vector d, int i)
{
pos = p;
dimensions = d;
imArrIndex = i;
}
void sprite::setPos(vector v)
{
pos = v;
}
vector sprite::getPos()
{
return pos;
}
void sprite::setDimensions(vector v)
{
dimensions = v;
}
vector sprite::getDimensions()
{
return dimensions;
}
void sprite::setImArrIndex(int i)
{
imArrIndex = i;
}
int sprite::getImArrIndex()
{
return imArrIndex;
}
void sprite::movePos(vector v)
{
pos.setX(pos.getX() + v.getX());
pos.setY(pos.getY() + v.getY());
}
and a derived class actor:
actor.hh:
#include <iostream>
#include "sprite.hh"
#include "vector.hh"
#ifndef ACTOR
#define ACTOR
class actor : public sprite
{
private:
public:
actor(vector p, vector d, int i) : sprite(p, d, i);
};
#endif
actor.cc:
#include <iostream>
#include "sprite.hh"
#include "actor.hh"
#include "vector.hh"
using namespace std;
actor::actor(vector p, vector d, int i) : sprite(p, d, i)
{
cout << "actor created\n";
}
Both classes use the vector class:
vector.hh:
#include <iostream>
#include <cmath>
#ifndef VECTOR
#define VECTOR
class vector
{
private:
int x;
int y;
public:
vector();
vector(int px, int py);
void setX(int px);
int getX();
void setY(int py);
int getY();
//get the pixel distance between this vector and a given vector
int getDistance(vector v);
};
#endif
vector.cc:
#include <iostream>
#include "vector.hh"
using namespace std;
vector::vector()
{
cout << "vector created\n";
}
vector::vector(int px, int py)
{
x = px;
y = py;
cout << "vector created\n";
}
void vector::setX(int px)
{
x = px;
}
int vector::getX()
{
return x;
}
void vector::setY(int py)
{
y = py;
}
int vector::getY()
{
return y;
}
//get the pixel distance between this vector and a given vector
int vector::getDistance(vector v)
{
return sqrt( pow(x - v.getX(), 2) + pow(y - v.getY(), 2) );
}
Right now I am just trying to get the constructor for the actor working in my main code with this section:
vector p(0,0);
vector d(0,0);
actor a(p, d, 1);
But I am getting this error when running my makefile:
g++ -c level.cc
In file included from level.cc:6:
actor.hh: In constructor ‘actor::actor(vector, vector, int)’:
actor.hh:15:53: error: expected ‘{’ at end of input
actor(vector p, vector d, int i) : sprite( p, d, i);
^
make: *** [makefile:11: level.o] Error 1
My makefile works just fine if I don't call the base constructor in the derived class so I'm sure its not an error in my makefile, but here is my makefile just in case:
FrogGame: frogmain.o game.o level.o sprite.o actor.o vector.o
g++ -std=c++0x -Wall -pedantic -o FrogGame frogmain.o game.o level.o sprite.o actor.o vector.o `sdl2-config --cflags --libs` -lSDL2_image
frogmain.o: frogmain.cc game.hh level.hh sprite.hh vector.hh
g++ -c frogmain.cc
game.o: game.cc level.hh sprite.hh actor.hh vector.hh
g++ -c game.cc
level.o: level.cc sprite.hh actor.hh vector.hh
g++ -c level.cc
actor.o: actor.cc vector.hh
g++ -c actor.cc
sprite.o: sprite.cc vector.hh
g++ -c sprite.cc
vector.o: vector.cc
g++ -c vector.cc
I'm assuming I have some syntax wrong in the constructor but can't seem to fix it, any suggestions?
In your header file, you have this line:
public:
actor(vector p, vector d, int i) : sprite(p, d, i); // Error!
I see what you're trying to do here, but this isn't legal C++ syntax. If you plan to declare the actor constructor but then define it in a .cpp file, which is what you're doing here, just write
actor(vector p, vector d, int i);
Then, in the .cpp file, as you've done, write
actor::actor(vector p, vector d, int i) : sprite(p, d, i)
{
cout << "actor created\n";
}
As you have it written right now, the C++ compiler sees the declaration of the actor constructor. When it then sees the initialization list (the : sprite(p, d, i) part), it thinks "oh, okay, you're defining the functions here." It's then mightily confused about why there's a semicolon instead of the actual body of the function, which is what the compiler error you're getting says.

Usage of warn_unused attribute for classes in C++

I have the following code:
test.cpp
#include <iostream>
using namespace std;
class [[warn_unused]] test{
int val;
public:
test() {
val = 0;
}
int getv() { return this->val; }
~test() {
cout << "end\n";
}
};
int main() {
test t1;
int t2 = t1.getv();
return 0;
}
I get the following warnings from the compiler on running g++ test.cpp. My g++ version is 9.2.1.
test.cpp:4:23: warning: ‘warn_unused’ attribute directive ignored [-Wattributes]
4 | class [[warn_unused]] test{
|
If I change the attribute to [[gnu::warn_unused]], then there are no warnings whether I use the class or not.

About compilation unit and Schwarz Counter and singleton pattern

I look this example and practice it:
https://novus.pixnet.net/blog/post/23784820
(The code is at the end)
The compile command is:
g++ sc.cc main.cc -o main.exe
I have 2 questions:
The Compilation Unit has 2 unit which consist of sc and main. (correct?)
I know this example (Schwarz Counter) is applied to iostream (C++).Singleton pattern looks suitable for this situation. Does Singleton pattern is better than Schwarz Counter in the case for unique and global (such as file io)?
Code:
sc.h
#include <fstream>
class Log {
friend class Initializer;
public:
void Print(const char* msg) {
std::fputs(msg, onlyFile_);
}
private:
static std::FILE* onlyFile_;
};
class Initializer {
public:
Initializer();
~Initializer();
private:
static int ref_;
};
extern Log log;
static Initializer init;
#endif
sc.cc
#include "s.h"
std::FILE* Log::onlyFile_ = 0;
int Initializer::ref_ = 0;
Initializer::Initializer() {
if (0 == ref_++) {
Log::onlyFile_ = fopen("somefile.txt", "w");
}
}
Initializer::~Initializer() {
if (0 == --ref_) {
fclose(Log::onlyFile_);
}
}
main.c
#include "s.h"
int main(int argc, char const *argv[]) {
log.Print("aa");
return 0;
}

Include issue: 'multiple definition', 'first defined here'

I have three files:
main.cpp
MyClass.cpp
MyClass.hpp
I have a library header file, "testLib.hpp", that I want to include in MyClass.hpp so that I can have one of testLib's objects be a class attribute.
I include MyClass.hpp in MyClass.cpp and in main.cpp. When attempting to compile the project, I get the following errors
MyClass.cpp multiple definition of 'testLib::testLib::function1()
obj/Release/main.o:main.cpp first defined here
MyClass.cpp multiple definition of 'testLib::testLib::function2()
obj/Release/main.o:main.cpp first defined here
and so on.
Both main.cpp and MyClass.cpp include MyClass.hpp (which includes testLib.hpp). Judging by the error, it looks like MyClass.cpp is attempting to include the library functions after they've already been included by main.cpp. However, I have include guards present in MyClass.hpp so I don't understand how it's trying to include MyClass.hpp twice.
Here's the code:
MyClass.hpp
#ifndef THIS_HEADER_H
#define THIS_HEADER_H
#include <stdint.h>
#include <iostream>
#include "testLib/testLib.hpp"
class MyClass
{
public:
void test();
int foo;
private:
uint32_t bar;
//I want to include an object from the library as part of this class
//TestLib::Device device;
};
#endif
MyClass.cpp
#include <stdio.h>
#include "MyClass.hpp"
void MyClass::test()
{
}
main.cpp
#include <iostream>
#include "MyClass.hpp"
using namespace std;
int main()
{
cout << "Hello world!" << endl;
return 0;
}
Any help would be greatly appreciated!
EDIT
I tried to hide the actual filenames to make the question more general and clear, but it seems like the problem might be resulting from 'testLib.hpp', which I did not write. That file is actually the following "sweep.hpp" file. I got the 'multiple definition of/first defined here' errors for each of the public functions in this file:
sweep.hpp
#ifndef SWEEP_DC649F4E94D3_HPP
#define SWEEP_DC649F4E94D3_HPP
/*
* C++ Wrapper around the low-level primitives.
* Automatically handles resource management.
*
* sweep::sweep - device to interact with
* sweep::scan - a full scan returned by the device
* sweep::sample - a single sample in a full scan
*
* On error sweep::device_error gets thrown.
*/
#include <cstdint>
#include <memory>
#include <stdexcept>
#include <vector>
#include <sweep/sweep.h>
namespace sweep {
// Error reporting
struct device_error final : std::runtime_error {
using base = std::runtime_error;
using base::base;
};
// Interface
struct sample {
const std::int32_t angle;
const std::int32_t distance;
const std::int32_t signal_strength;
};
struct scan {
std::vector<sample> samples;
};
class sweep {
public:
sweep(const char* port);
sweep(const char* port, std::int32_t bitrate);
void start_scanning();
void stop_scanning();
bool get_motor_ready();
std::int32_t get_motor_speed();
void set_motor_speed(std::int32_t speed);
std::int32_t get_sample_rate();
void set_sample_rate(std::int32_t speed);
scan get_scan();
void reset();
private:
std::unique_ptr<::sweep_device, decltype(&::sweep_device_destruct)> device;
};
// Implementation
namespace detail {
struct error_to_exception {
operator ::sweep_error_s*() { return &error; }
~error_to_exception() noexcept(false) {
if (error) {
device_error e{::sweep_error_message(error)};
::sweep_error_destruct(error);
throw e;
}
}
::sweep_error_s error = nullptr;
};
}
sweep::sweep(const char* port)
: device{::sweep_device_construct_simple(port, detail::error_to_exception{}), &::sweep_device_destruct} {}
sweep::sweep(const char* port, std::int32_t bitrate)
: device{::sweep_device_construct(port, bitrate, detail::error_to_exception{}), &::sweep_device_destruct} {}
void sweep::start_scanning() { ::sweep_device_start_scanning(device.get(), detail::error_to_exception{}); }
void sweep::stop_scanning() { ::sweep_device_stop_scanning(device.get(), detail::error_to_exception{}); }
bool sweep::get_motor_ready() { return ::sweep_device_get_motor_ready(device.get(), detail::error_to_exception{}); }
std::int32_t sweep::get_motor_speed() { return ::sweep_device_get_motor_speed(device.get(), detail::error_to_exception{}); }
void sweep::set_motor_speed(std::int32_t speed) {
::sweep_device_set_motor_speed(device.get(), speed, detail::error_to_exception{});
}
std::int32_t sweep::get_sample_rate() { return ::sweep_device_get_sample_rate(device.get(), detail::error_to_exception{}); }
void sweep::set_sample_rate(std::int32_t rate) {
::sweep_device_set_sample_rate(device.get(), rate, detail::error_to_exception{});
}
scan sweep::get_scan() {
using scan_owner = std::unique_ptr<::sweep_scan, decltype(&::sweep_scan_destruct)>;
scan_owner releasing_scan{::sweep_device_get_scan(device.get(), detail::error_to_exception{}), &::sweep_scan_destruct};
auto num_samples = ::sweep_scan_get_number_of_samples(releasing_scan.get());
scan result;
result.samples.reserve(num_samples);
for (std::int32_t n = 0; n < num_samples; ++n) {
auto angle = ::sweep_scan_get_angle(releasing_scan.get(), n);
auto distance = ::sweep_scan_get_distance(releasing_scan.get(), n);
auto signal = ::sweep_scan_get_signal_strength(releasing_scan.get(), n);
result.samples.push_back(sample{angle, distance, signal});
}
return result;
}
void sweep::reset() { ::sweep_device_reset(device.get(), detail::error_to_exception{}); }
} // ns
#endif
A simplified version of your problem:
buggy.hpp
int function() { return 0; }
main.cpp
#include "buggy.hpp"
int main() { return 0; }
other.cpp
#include "buggy.hpp"
The problem is that buggy.hpp is defining function, not just declaring. Once the header inclusion is expanded, that means function is declared in both main.cpp and other.cpp - and that is not allowed.
The fix is to declare function as inline which allows the function to be declared in multiple translation units.
inline int function() { return 0; }
In fact, allowing multiple definitions is the only meaning of inline to the C++ standard. Compilers may treat it as a hint that the function body may be expanded inline. Good ones won't; they are better at making that sort of decision that programmers).

How to get a whole row from database using SOCI into user-defined object type?

I extended my class (from this question: How to get a whole row from database using SOCI?) to have two private members, also added getters and setters. But when compiling my program, I have errors again.
myClass.h
#include <soci.h>
#include <postgresql/soci-postgresql.h>
#include <string>
class MyClass
{
public:
MyClass();
MyClass(int i, std::string);
void setI(int i);
int getI() const;
void setS(std::string s);
std::string getS() const;
private:
int i_;
std::string s_;
};
namespace soci
{
template <>
struct type_conversion<MyClass>
{
typedef MyClass base_type;
static void from_base(int i, std::string s, soci::indicator ind, MyClass & mi)
{
if (ind == soci::i_null)
{
throw soci_error("Null value not allowed for this type");
}
mi.setI(i);
mi.setS(s);
}
static void to_base(const MyClass & mi, int & i, std::string &s, soci::indicator & ind)
{
i = mi.getI();
s = mi.getS();
ind = soci::i_ok;
}
};
}
myClass.cpp
#include "myClass.h"
MyClass::MyClass()
{
}
MyClass::MyClass(int i, std:string s)
{
this->i_ = i;
this->s_ = s;
}
int MyClass::getI() const
{
return this->i_;
}
void MyClass::setI(int i)
{
this->i_ = i;
}
std::string MyClass::getS() const
{
return this->s_;
}
void MyClass::setS(std::string s)
{
this->s_ = s;
}
myClassTest.cpp
#include <iostream>
#include "myClass.h"
int main(int argc, char **argv)
{
soci::session sql;
sql.open(soci::postgresql, "dbname=mydb user=postgres password=postgrespass");
MyClass i;
sql << "SELECT id, name FROM person;", soci::into(i);
std::cout << i.getI() << " " << i.getS();
sql.close();
return 0;
}
I compile it like this:
g++ myClassTest.cpp myClass.h myClass.cpp -o App -lsoci_core
-lsoci_postgresql -ldl -lpq -I /usr/local/include/soci -I /usr/include/postgresql
And the errors I got are:
In file included from /usr/local/include/soci/into-type.h:13:0,
from /usr/local/include/soci/blob-exchange.h:12,
from /usr/local/include/soci/soci.h:18,
from myClass.h:1,
from myClassTest.cpp:2:
/usr/local/include/soci/exchange-traits.h: In instantiation of ‘soci::details::exchange_traits<MyClass>’:
/usr/local/include/soci/into.h:29:60: instantiated from ‘soci::details::into_type_ptr soci::into(T&) [with T = MyClass, soci::details::into_type_ptr = soci::details::type_ptr<soci::details::into_type_base>]’
myClassTest.cpp:27:65: instantiated from here
/usr/local/include/soci/exchange-traits.h:35:5: error: incomplete type ‘soci::details::exchange_traits<MyClass>’ used in nested name specifier
In file included from /usr/local/include/soci/into.h:13:0,
from /usr/local/include/soci/soci.h:22,
from myClass.h:1,
from myClassTest.cpp:2:
/usr/local/include/soci/type-conversion.h: In member function ‘void soci::details::conversion_into_type<T>::convert_from_base() [with T = MyClass]’:
myClassTest.cpp:34:1: instantiated from here
/usr/local/include/soci/type-conversion.h:59:9: error: no matching function for call to ‘soci::type_conversion<MyClass>::from_base(soci::type_conversion<MyClass>::base_type&, soci::indicator&, MyClass&)’
/usr/local/include/soci/type-conversion.h:59:9: note: candidate is:
myClass.h:28:21: note: static void soci::type_conversion<MyClass>::from_base(int, std::string, soci::indicator, MyClass&)
myClass.h:28:21: note: candidate expects 4 arguments, 3 provided
myClass.cpp:8:28: error: found ‘:’ in nested-name-specifier, expected ‘::’
Have you tried SOCI 3.2? I did something similar with this version which worked pretty well:
vector<Entry> Entry::findByName(string name) {
Entry entry;
vector<Entry> entries;
//Starting a connection to database
session sql(firebird, "service=/srv/firebird/registry.gdb user=SYSDBA password=password");
//Querying data using a prepared statement and place data into a Entry object
statement st = (sql.prepare <<
"select NAME, ADDRESS, PHONE from ENTRY WHERE NAME like '%' || :NAME || '%'",
into(entry), use(name));
st.execute();
//Checking if we can fetch a row from resultset
while (st.fetch())
{
//Pushing the object with mapped data into the entries vector
entries.push_back(entry);
}
return entries;
}
You can change this to:
Entry Entry::findByName(string name) {
Entry entry;
//Starting a connection to database
session sql(firebird, "service=/srv/firebird/registry.gdb user=SYSDBA password=password");
//Querying data using a prepared statement and place data into a Entry object
statement st = (sql.prepare <<
"select NAME, ADDRESS, PHONE from ENTRY WHERE NAME like '%' || :NAME || '%'",
into(entry), use(name));
st.execute();
//Checking if we can fetch a row from resultset
if (st.fetch())
{
return entry;
}
return NULL;
}
Note: Both of them are static classes of my model class which implements a custom version of ActiveRecord.