Serialize a c++ virtual class without boost? - c++

I want to dump some data into files, then load it back. i do care the performance.
I don't want to use boost serialization. I want to serialize and load it back by myself.
if the struct is POD, i think this will be quite easy, but there is some problems if my data class has virtual function, like this:
// base.h
#include <string>
class Base {
public:
virtual std::string show_string() = 0;
int b;
};
class A : public Base {
public:
std::string show_string() override { return "this is A"; } // here is the problem, virtual pointer
int a;
};
there is a data producer, dump the data into file, like this:
#include <fstream>
#include <iostream>
#include "./base.h"
using namespace std;
int main() {
std::fstream f;
f.open("a.bin", std::ios::out | std::ios::binary);
for (int i = 0; i < 10; ++i) {
A a;
a.a = i;
a.b = i - 1;
f.write((char*)&a, sizeof(a));
}
f.close();
}
then i need to load it back, my code looks like:
#include "./base.h"
#include <fstream>
#include <iostream>
using namespace std;
int main() {
std::fstream f;
f.open("a.bin", std::ios::in | std::ios::binary);
char buff[65536];
f.read(buff, sizeof(buff));
A* a = (A*)buff;
cout << a->show_string() << endl;
f.close();
}
this will crashed, i think the problem is the virtual pointer.
for virtual pointer, i think i need a correct serialize method.
could you help on this? how can i make this work?

Related

C++ managing ifstream in a container with different object types

I have created several different objects of Dog and Cat inside the container animalColl. I am trying to workout how to adjust the file stream that is read so that if the object in the container is Cat then the catfile is used and Dog uses the dogfile. Currently, only the animalType passed into the parameter is used. Is it possible to choose which file stream is used based on the object type?
void Animal::load(std::string animalType)
{
std::string file = animalType + ".csv";
std::ifstream lstream(file);
for (Animal *a : animalColl)
{
a->load(lstream); //uses one stream to read all the different objects in container
}
}
Unfortunately, I'm not sure why you're calling load recursively, so I can't give an exact answer. I think the answer you're looking for is going to be using polymorphism. This is a basic example:
animal.h
#ifndef ANIMAL_H
#define ANIMAL_H
#include <iostream>
#include <string>
class Animal
{
public:
void load()
{
std::string fileName = this->getAnimalFilename() + ".csv";
std::cout << "fileName = " << fileName << std::endl;
}
protected:
virtual std::string getAnimalFilename() { return "Animal"; }
};
#endif //ANIMAL_H
dog.h
#ifndef DOG_H
#define DOG_H
#include <string>
#include <iostream>
class Dog : public Animal
{
protected:
virtual std::string getAnimalFilename() { return "Dog"; }
};
#endif //DOG_H
cat.h
#ifndef CAT_H
#define CAT_H
#include <iostream>
#include <string>
class Cat : public Animal
{
protected:
virtual std::string getAnimalFilename() { return "Cat"; }
};
#endif //CAT_H
And an example usage (note that you MUST use a pointer to the base class to get the overriding feature of polymorphism, and you MUST declare a function as virtual to override it in the derived class).
EDIT: The below main.cpp was edited to use smart pointers because it used raw pointers and caused a memory leak, pointed out by #ArchbishopOfBanterbury.
main.cpp
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include "animal.h"
#include "dog.h"
#include "cat.h"
int main(int argc, char *argv[])
{
std::vector<std::unique_ptr<Animal>> animalColl;
animalColl.emplace_back(new Dog());
animalColl.emplace_back(new Cat());
animalColl.emplace_back(new Cat());
animalColl.emplace_back(new Dog());
for (auto &a : animalColl) {
a->load();
}
return 0;
}
And the output:
fileName = Dog.csv
fileName = Cat.csv
fileName = Cat.csv
fileName = Dog.csv
The basic idea is that you use the keyword virtual to override the behavior when using a pointer to the base class. So in my example, getAnimalFilename is overrided in the Dog and Cat class to return the correct string, rather than passing it into the load() function. Does that help any? Go ahead and reply to this comment and I'll try to help as much as I can.
There are a few ways you could do this. You could add an element to the class called name and strcmp this to check for type for instance:
struct Animal {
};
struct Dog : Animal {
string name = "Dog";
};
struct Cat : Animal {
string name = "Cat";
};
int main() {
Dog d;
if("Dog" == d.name) {
//do something
}
else if("Cat" == d.name) {
//do something else
}
}
Another way to do this would be if you know the type of another object, or are willing to create another object of type Dog or Cat, you can compare those types using typeid(obj) for instance, using the same structs as above:
int main(int argc, const char * argv[]) {
Dog d, o;
if (typeid(d) == typeid(o)) {
//do something
}
else {
//do something else
}
}

Trouble accessing members of class from a separate class C++

I am having a lot of trouble with a relatively simple task. I have two header files, input.h and grains.h, both of which have classes defined within them. I have included all header and source files for this project below.
My problem is that when input->from_file(fname) is executed, the value printed to the screen is correct, let's say it is 4. Then when it moves on the grains->get_pars(), the value printed to the screen is garbage, usually ~605937280. I know this type of garbage value is indicative of the variable not being set, but I don't understand how it is not being set.
My goal is to have input.cpp read some file for some parameters, which are important to grains.cpp, and pass them along. I thought that derived classes would do the trick, but something is not working right. Any hints on what I have done wrong would be greatly appreciated. Also, any suggestions to achieve this goal aside from the one I have presented are very welcome, thanks. Note, the code as shown compiles just fine.
//input.h
#ifndef Input_H
#define Input_H
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <cstring>
class Input {
protected:
int in_grains;
public:
void from_file(std::string);
};
#endif
//grains.h
#ifndef Grains_H
#define Grains_H
#include "input.h"
class Grains : protected Input {
protected:
int grains;
public:
void get_pars(void);
};
#endif
//input.cpp
#include "input.h"
void Input::from_file(std::string infile)
{
std::ifstream input(infile.c_str());
std::istringstream iss;
std::string line, keyword;
char arg1[50], arg2[50], arg3[50];
while(std::getline(input,line)) {
iss.clear();
iss.str(line);
iss >> keyword >> arg1 >> arg2 >> arg3;
if ((keyword == "GRAINS") || (keyword == "Grains") || (keyword == "grains")) {
this->in_grains = atoi(arg1);
}
}
fprintf(stdout,"%i\n",in_grains );
}
//grains.cpp
#include "grains.h"
void Grains::get_pars(void)
{
this->grains = in_grains;
fprintf(stdout,"%i\n",grains );
}
//main.cpp
#include "input.h"
#include "grains.h"
int main(int nargs, char *argv[])
{
Input obj1;
Input *input = &obj1;
Grains obj2;
Grains *grains = &obj2;
std::string fname = argv[1];
input->from_file(fname.c_str());
grains->get_pars();
return 0;
}
I am guessing that when you execute:
input->from_file(fname.c_str());
grains->get_pars();
you are expecting the in_grains from input to be available as grains->grains. input and grains are two different objects. in_grains has not been set on the object grains points to.
Perhaps you meant to use:
int main(int nargs, char *argv[])
{
Grains obj;
Input *input = &obj;
Grains *grains = &obj;
std::string fname = argv[1];
input->from_file(fname.c_str());
grains->get_pars();
return 0;
}
However, to use that, you have to make Input a public base class of Grain, not a protected base class.
class Grains : public Input {

how to initialize static reference to std::ofstream?

I have a class Cl with public member
static std::ofstream &_rout;
In main file
ofstream out("output.txt");
ofstream& Cl::_rout(out);
But I have a compilation error: illegal definition or redefinition.
How can I correct it?
Try this.
Logger.h
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
class Logger{
public:
static void open( const string & logFile);
static void close();
// write message
static void write( const string & message);
private:
Logger();
ofstream fileStream;
//Logger instance (singleton)
static Logger instance;
};
Logger.cpp
#include "Logger.h"
Logger Logger::instance;
Logger::Logger(){}
void Logger::open( const string& logFile){
instance.fileStream.open(logFile.c_str());
}
void Logger::close(){
instance.fileStream.close();
}
void Logger::write(const string& message){
ostream& stream = instance.fileStream ;
stream << message<< endl;
}
main.cpp
#include "Salida/Logger.h"
int main(){
Logger::open(path);
Logger::write("text");
Logger::close();
return 0;
}
You can only set the reference at the static/global scope
#include<CL.h>
ofstream& Cl::_rout(out);
int main() {
// ...
}
It is not possible to re-set a reference after it was declared (and initialized). You could achieve what you are after by using pointers instead of references:
class Cl {
static std::ofstream* _rout;
};
std::ofstream* CL::_rout = NULL;
int main() {
ofstream out("output.txt");
Cl::_rout = &out;
}
Note that the pointer will be valid only until out goes out of scope. If this is an issue, allocate the memory dynamically:
ofstream* out = new ofstream("output.txt");
Cl::_rout = out;
And don't forget to delete it when you no longer need the object to avoid memory leaks
Well, you could use the following approach:
#include <fstream>
class CI
{
public:
static std::ofstream &_rout;
};
static std::ofstream out("output.txt");
std::ofstream& CI::_rout = out;
int main()
{
}
The problem with this, however, is that the name of the output file is fixed (hard-coded into the program).
I suggest that you use a pointer instead of a reference:
#include <cstddef>
#include <fstream>
class CI
{
public:
static std::ofstream *_rout;
};
std::ofstream* CI::_rout = NULL;
int main()
{
const char *output_file = "output.txt";
std::ofstream out(output_file);
CI::_rout = &out;
}

Error serializing an abstract class with boost

I'm trying to serialize my data structures in order to write them to a tcp socket.
So far I found that my problem is the serialization. I even tried to use
BOOST_SERIALIZATION_ASSUME_ABSTRACT(T)
but I can't find any working example similar to my program and how to implement it correctly.
Here are some of the links that I have visited:
http://programmers-blog.com/category/c-c
http://www.boost.org/doc/libs/1_48_0/libs/serialization/doc/tutorial.html#simplecase
http://en.highscore.de/cpp/boost/serialization.html#serialization_class_hierarchies
My data structures are a little more complicated then this one but let's assume that I have the following structure
Coordinate.h
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
class Coordinate {
public:
Coordinate() {}
Coordinate(int c) : c(c) {}
int get(void) { return c; }
std::string toString(void);
private:
int c;
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive &ar, const unsigned int version) {
ar & this->c;
}
};
Move.h
class Coordinate;
#include "Coordinate.h"
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
class Move {
public:
Move() {}
~Move() {}
Coordinate* getCoordinate(void) {return this->destination; }
virtual bool isJump(void) = 0;
protected:
Coordinate *destination;
private:
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive &ar, const unsigned int version) {
ar & this->destination;
}
};
MoveNormal.h
class Coordinate;
#include "Move.h"
#include "Coordinate.h"
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
class MoveNormal : public Move {
public:
MoveNormal() {}
MoveNormal(Coordinate *destination) { this->destination = destination; }
~MoveNormal() {}
virtual bool isJump(void);
private:
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive &ar, const unsigned int version) {
ar & boost::serialization::base_object<Move>(*this);
}
};
The virtual methods are defined in here.
MoveNormal.cpp
#include "MoveNormal.h"
bool MoveNormal::isJump(void) {
return false;
}
My main.cpp looks like this:
#include "Coordinate.h"
#include "Move.h"
#include "MoveNormal.h"
#include <fstream>
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
int main(int argc, char *argv[]) {
Coordinate *c = new Coordinate(10);
// This runs OK
/*
{
std::ofstream ofs("f.txt");
boost::archive::text_oarchive oa(ofs);
oa << c;
}
Coordinate *d;
{
std::ifstream ifs("f.txt");
boost::archive::text_iarchive ia(ifs);
ia >> d;
}
std::cout << "c.get(): " << c->get() << std::endl;
std::cout << "d.get(): " << d->get() << std::endl;
*/
// This is where I get my error
Move *m = new MoveNormal(c);
{
std::ofstream ofs("f.txt");
boost::archive::text_oarchive oa(ofs);
oa << m; // Line where the error occurs
}
return 0;
}
But when I run the program I get the following error:
Unhandled exception at 0x76dbb9bc in Test.exe: Microsoft C++ exception: boost::archive::archive_exception at memory location 0x001df078..
I'm using VS2010, and Boost 1.48.0.
This is a little bit weird but I'm going to answer my own question. I just figured out how to make my example above work.
Here it goes the solution. Everytime we need to serialize a class that inherits attributes from another class we need to use the macro:
BOOST_CLASS_EXPORT(T)
According to the boost serialization doc
BOOST_CLASS_EXPORT in the same source module that includes any of the archive class headers will instantiate code required to serialize polymorphic pointers of the indicated type to the all those archive classes. If no archive class headers are included, then no code will be instantiated.
Note that the implemenation of this functionality requires that the BOOST_CLASS_EXPORT macro appear after and the inclusion of any archive class headers for which code is to be instantiated.
So in my case my main.cpp file is now:
#include <fstream>
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
#include <boost\serialization\export.hpp>
#include "Coordinate.h"
#include "Move.h"
#include "MoveNormal.h"
BOOST_CLASS_EXPORT(MoveNormal)
int main(int argc, char *argv[]) {
Coordinate *c = new Coordinate(150);
Move *m = new MoveNormal(c);
std::cout << "m.getDestination().get(): " << m->getDestination()->get() << std::endl;
{
std::ofstream ofs("f.txt");
boost::archive::text_oarchive oa(ofs);
oa << m;
}
Move *n;
{
std::ifstream ifs("f.txt");
boost::archive::text_iarchive ia(ifs);
ia >> n;
}
std::cout << "n.getDestination().get(): " << n->getDestination()->get() << std::endl;
return 0;
}
Just make sure that you include all the boost archives you need before you use the export MACRO.
To finish my project besides the serialization I need to write them to a tcp socket using boost::asio.
So let's assume that I have a connection header like this one and that now I have another class called MoveJump defined in my MoveJump.h
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
#include "Coordinate.h"
#include "Move.h"
class MoveJump : public Move {
public:
MoveJump() {}
MoveJump(Coordinate *c) { this->destinatio = c; }
~MoveJump() {}
virtual bool isJump(void);
private:
friend class boost::serialization::access;
template<typename Archive>
void serializize(Archive &ar, const unsigned int version) {
ar & boost::serialization::base_object<Move>(*this);
}
};
Now to serialize these structures my main look like this
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
#include <boost\serialization\export.hpp>
#include <fstream>
#include "Coordinate.h"
#include "Move.h"
// And now we register all the possible Moves
#include "MoveNormal.h"
BOOST_CLASS_EXPORT(MoveNormal)
#include "MoveJump.h"
BOOST_CLASS_EXPORT(MoveJump)
int main(int argc, char *argv[]) {
Coordinate *c = new Coordinate(10);
Move *m = new MoveNormal(c);
Coordinate *d = new Coordinate(15);
Move *j = new MoveJump(d);
{
std::ofstream ofs("m.txt");
boost::archive::text_oarchive oa(ofs);
oa << m;
}
{
std::ofstream ofs("j.txt");
boost::archive::text_oarchive oa(ofs);
oa << j;
}
}
The trick is to register the classes that will be serialized when we have the pointer to the base class.
If inside my Move.h I have more pointers to other base classes, which I do in my project, we need to include in the main all the headers and register all the possible classes that expand the base class.
I hope this helps someone who might have similar problems in the future.
Feel free to present new possible solutions.
Thanks
General speaking, you can simply use BOOST_CLASS_EXPORT to register all the classes, or you can use BOOST_SERIALIZATION_ASSUME_ABSTRACT for the super class, and use member function register_type of "archive" together. see : How to serialize derived template classes with Boost.serialize? for details.(sorry for my poor english:))

Variable scope error when declaring friend function

Friend functions can't access variables of the classes
I'm having a problem with several friend functions not being able to access the variables in classes where they have been declared as friends.
The actual error text is:
error: 'fid' was not declared in this scope. this repeats for the other private variables.
The same error is given for three functions, read, negative, and write.
A couple of notes:
1) This lab requires that I write the code so that the functions can be used by both classes.
I'm compiling this in windows with code::blocks using g++ and I've also tried compiling my code in ubuntu using g++ from the terminal using the -g flag and I get the same error both times.
Any suggestions you have would be greatly appreciated.
Header File
#ifndef PXMUTILS_H
#define PXMUTILS_H
#include <cstdio>
#include <cstdlib>
#include <string>
#include <sstream>
#include <vector>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <string.h>
using namespace std;
typedef unsigned char uchar;
class pgm
{
public:
pgm();
~pgm();
void read(string &);
void negative();
void write(string);
friend void read (const string &);
friend void write(string);
friend void negative();
private:
int nr;
int nc;
int mval;
int ftyp;
string fid;
uchar **img;
};
class ppm
{
public:
ppm();
~ppm();
void read(string &);
void negative();
void write(string);
friend void read (const string &);
friend void write (string);
friend void negative ();
private:
int nr;
int nc;
int mval;
int ftyp;
string fid;
uchar **img;
};
#endif
C++ program
#include <cstdio>
#include <cstdlib>
#include <string>
#include <sstream>
#include <vector>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <string.h>
#include "pxmutils.h"
using namespace std;
typedef unsigned char uchar;
uchar ** newimg(int nr, int nc, int ftyp)
{
uchar **img=new uchar *[nr];
img[0]=new uchar [nr*nc*ftyp];
for(int i=1; i<nr; i++)
{
img[i]=img[i-1]+nc*ftyp;
}
return img;
}
void deleteimg(uchar **img)
{
if(img)
{
if(img[0])
{
delete [] img[0];
}
delete [] img;
}
}
void read (const string &fname)
{
ifstream fin(fname.c_str(), ios::in);
if(!fin.is_open())
{
cerr<<"Could not open "<<fname<<endl;
exit(0);
}
fin >>fid
>>nc
>>nr
>>mval;
while (fin.get() != '\n') { /*skip to EOL */ }
img=newimg(nr, nc);
fin.read((char *)img[0], nr*nc);
fin.close();
}
void set_cmap(string mname)
{
}
void negative()
{
for(int i=0; i<nr; i++)
{
for(int j=0; j<nc; j++)
{
int t=img[i][j];
img[i][j]=(255-t);
}
}
}
void write(string fname)
{
ofstream fout (fname.c_str(), ios::out);
size_t dp;
if ((dp = fname.rfind(".pgm")) != string::npos)
{
fout<<"P5"<<endl;
}
if((dp= fname.rfind(".ppm")) != string::npos)
{
fout<<"P6"<<endl;
}
fout<<nc<<" "<<nr<<endl;
fout<<mval<<endl;
for(int i=0; i <nr; i++)
{
for (int j=0; j<nc; j++)
{
fout<<img[i][j]<<" ";
}
fout<<endl;
}
fout.close();
}
pgm::pgm()
{
nr=0;
nc=0;
mval=0;
ftyp=1;
fid="";
img=NULL;
}
pgm::~pgm()
{
deleteimg(img);
}
ppm::ppm()
{
nr=0;
nc=0;
mval=0;
ftyp=1;
fid="";
img=NULL;
}
ppm::~ppm()
{
deleteimg(img);
}
Program to test functions
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
#include "pxmutils.h"
int main(int argc, char *argv[])
{
if (argc == 1) {
cerr << "No input file specified!\n";
exit(0);
}
string fname = argv[1];
size_t dp;
if ((dp = fname.rfind(".pgm")) == string::npos) {
cout << "PGM error: file suffix " << fname
<< " not recognized\n";
exit(0);
}
fname.erase(dp);
pgm img_g;
ppm img_c;
img_g.read(fname+".pgm");
if (argc == 3)
img_c.set_cmap(argv[2]);
img_c = img_g;
img_g.negative();
img_g.write(fname+"_n.pgm");
img_c.write(fname+"_c.ppm");
}
fin >>fid
>>nc
>>nr
>>mval;
while (fin.get() != '\n') { /*skip to EOL */ }
In this code, fid, nc, nr etc are undefined. You need to use the class instance to be able to access them, they don't exist by themselves.
Your functions don't accept the class objects as parameters, so how are you going to read into them?
You should have another think of your design. It is best to avoid friend functions if possible,
You need to go a bit back to basics. When you define non-static members of a class you are defining attributes or operations of the objects of the class, but those attributes don't exist by themselves, only as part of the instances of the class.
This concept is orthogonal to access and access specifiers, that is, this is so regardless of the members being public, protected or private. Once you have an instance, when your try to access those members the access specifiers come into play, and there is where friendship comes into play: it will grant your code access to members that would otherwise be inaccessible (private or protected outside of the inheritance hierarchy).
The problem in your code is that you don't have an object, and thus cannot access the members of the object. You will need to either create or pass an object of the appropriate type to the functions.
There are other problems in the code, like for example, the memory allocations inside newimg look a little suspicious (what were you intending to allocate?) but that is outside of the scope of this question.