Using Metadata/Inheritance to factor out code across multiple classes - c++

I have two classes that will represent two very simple databases, and each has a "Save" function which will write what's in the class to a file. Since the code within the "Save" function is very similar, I was wondering if I could factor it out.
One of my colleagues said this might be possible with inheritance and/or metadata, so I tried looking into it myself with Google. However, I couldn't find anything that was helpful and am still unsure if what I want to do is even possible.
If it's possible to factor out, then I think I'd need to have another class or function know about each class's types and iterate through them somehow (metadata?). It would check the type of every data, and depending on what the type is, it would make sure that it's correctly output to the text file.
(I know data like name, age, etc. should be private, but to keep this simple I just had everything be public)
class A
{
public:
A() : name(""), age(0) {};
void Save(void)
{
std::string filename = "A.txt";
std::string data;
data += name + "\n";
data += std::to_string(age) + "\n";
std::ofstream outfile(filename);
outfile.write(data.c_str(), data.size());
outfile.close();
}
std::string name;
int age;
};
class B
{
public:
B() : ID(0), points(0) {};
void Save(void)
{
std::string filename = "B.txt";
std::string data;
data += std::to_string(ID) + "\n";
data += std::to_string(points) + "\n";
std::ofstream outfile(filename);
outfile.write(data.c_str(), data.size());
outfile.close();
}
int ID;
int points;
};
int main(void)
{
A a;
B b;
a.name = "Bob"; a.age = 20;
b.ID = 4; b.points = 95;
a.Save();
b.Save();
return 0;
}

A possible solution could be to use metaprogramming (not sure what you mean by metadata), i.e. templates to reuse the common parts
template<typename T1, typename T2>
void TSave(const std::string fname, const T1& p1, const T2& p2) {
std::string filename = fname;
std::stringstream data;
data << p1 << "\n";
data << p2 << "\n";
std::ofstream outfile(filename);
outfile.write(data.str().c_str(), data.str().size());
outfile.close();
}
class A {
...
void Save(void) {
TSave("A.txt", name, age);
}
std::string name;
int age;
};
class B {
...
void Save(void) {
TSave("B.txt", ID, points);
}
int ID;
int points;
};
Live Example

What you are looking for is serialization: saving objects to a file (and one day or another, restore the objects).
Of course, you could write your own serialization framework, and Marco's answer is an interesting start in that direction. But alternatively, you could consider existing libraries, such as boost::serialization :
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
class A {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & name;
ar & age;
}
...
};
class B {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & ID;
ar & points;
}
...
};
main() {
A a;
B b;
...
{
std::ofstream ofs("myfile");
boost::archive::text_oarchive arch(ofs);
arch << a << b;
}
}
As you see, it's still needed to say what's to be written to the file. However, the code is simplified : you don't have to worry about file management and transformation of data. And it works also with standard containers.
You won't find a C++ trick that automatically determines for a class what's to be saved. Two reasons for that:
C++ allows metaprogramming, but it is not reflexive: there are no standard process to find out at execution time which members compose a class.
In an object, some data can be transient, i.e. it means only something at the time of the execution and depends on the context. For example pointers: you could save the value of a pointer to a file, but it will mean nothing when you reload it later (the pointer is only valid until you free the object). The proper way would be to save the object that is pointed to (but where, when, how?).

Related

Pass data from object in class A to class B

New to classes and objects in c++ and trying to learn a few basics
I have the class TStudent in which the Name, Surname and Age of student are stored, also I have the constructor which is accessed in main and inserts in the data.
What I want to do is: having the class TRegistru, I have to add my objects data in it, in a way that I can store it there, then I could save the data in data.bin and free the memory from the data, then I want to put the data back in the class and print it out.
The question is: In what way & what is the best way to add my objects in the second class, so that I could eventually work with them in the way I've described in the comments, so that I won't have to change nothing in main
Here's my code so far:
#include <iostream>
using namespace std;
class TStudent
{
public:
string Name, Surname;
int Age;
TStudent(string name, string surname, int age)
{
Name = name;
Surname = surname;
Age = age;
cout <<"\n";
}
};
class TRegistru : public TStudent
{
public:
Tregistru()
};
int main()
{
TStudent student1("Simion", "Neculae", 21);
TStudent student2("Elena", "Oprea", 21);
TRegistru registru(student1);//initialising the object
registru.add(student2);//adding another one to `registru`
registru.saving("data.bin")//saving the data in a file
registru.deletion();//freeing the TRegistru memory
registru.insertion("data.bin");//inserting the data back it
registru.introduction();//printing it
return 0;
}
Hence the question is about passing data from A to B, I will not comment on the file handling portion.
This can be done in multiple ways, but here is one of the simplest and most generic. By calling TRegistru::toString() you serialize every TStudent added to TRegistru into a single string which then can be easily written to a file.
Demo
class TStudent
{
public:
std::string Name, Surname;
int Age;
std::string toString() const
{
return Name + ";" + Surname + ";" + to_string(Age);
}
};
class TRegistru
{
public:
void add(const TStudent& student)
{
students.push_back(student);
}
void deletion()
{
students.clear();
}
std::string toString() const
{
std::string ret{};
for(const auto& student : students)
{
ret += student.toString() + "\n";
}
return ret;
}
std::vector<TStudent> students;
};

Trying to use boost::serialization of Boost::any

I am trying to work with boost::serialisation for saving and loading some objects.
So far from the boost::tutorial I have managed to do things work for all the different stl stuff (vectors, pairs, lists etc), for derived classes, for boost multi-arrays and other things, but I am stuck trying to work around how to serialize a boost::any vector.
Let me say in advance that I found and checked in the forum some posts for boost::varian serialization and even one for boost::any (which even has an almost identical title) but yet again I didn't manage to solve my problems.
So let me go with a small example:
Say I have this class:
class class_2
{
private:
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & degrees;
ar & minutes;
ar & seconds;
for( std::vector<boost::any>::iterator it = v_any.begin() ; it != v_any.end() ; ++it ) {
//Trying to cast all the possible types that may enter the
//boost any vector, for the sake of this example let's just
//say that we will only pass a second class called name class_1
//to the boost::any vector and we only have to cast this class
if (it->type() == typeid(class_1)){
class_1 lan = boost::any_cast< class_1 > (*it );
ar & (lan);
}
}// for boost::any*/
} //serialization
public:
int degrees;
int minutes;
float seconds;
class_2(){};
class_2(int d, int m, float s) :
degrees(d), minutes(m), seconds(s)
{}
std::vector<boost::any> v_any;
};
And to be more precise the class_1 that we expect for this small example to exist inside the boost::any vector is the following class:
class class_1
{
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & a;
}
public:
class_1(){};
int a;
};
So when I compile the above code with a main function where I save and load an object of class_2 that containts inside the boost::any vector an object of class_1 everything compiles and even runs:
int main() {
class_1 alpha;
class_2 beta;
alpha.a=5;
beta.v_any.push_back(alpha);
std::ofstream ofs("file");
// save data to archive
{
boost::archive::text_oarchive oa(ofs);
// write class instance to archive
oa << beta;
// archive and stream closed when destructors are called
}
// ... some time later restore the class instance to its orginal state
class_2 newclass;
{
// create and open an archive for input
std::ifstream ifs("file");
boost::archive::text_iarchive ia(ifs);
// read class state from archive
ia >> newclass;
// archive and stream closed when destructors are called
}
return 0;
}
Yet again the loaded newclass object has an empty boost::any vector with nothing saved inside.
So my question is what am I doing wrong in the above code and what should I change in order to serialize the boost::any vector correctly..?
Any help/suggestion would be very appreciated.

C++: Monitoring multiple value types

I want to write a class that can monitor a bunch of different values for easy debugging. Imagine setting "watches" in a visual debugger. I'm picturing something like this:
struct Foo {
int x = 0;
std::string s = "bar";
};
int main() {
Foo f;
ValueMonitor::watch("number", &f.x);
ValueMonitor::watch("string", &f.s);
for (int i = 0; i < 10; ++i) {
++f.x;
if (i > 5) {
f.s = "new string";
}
// print the current value of the variable with the given key
// these should change as the loop goes on
ValueMonitor::print("number");
ValueMonitor::print("string");
// or
ValueMonitor::printAll();
// obviously this would be unnecessary in this example since I
// have easy access to f, but imagine monitoring different
// values from all over a much larger code base
}
}
Then these could be easily monitored somewhere in the application's GUI or whatever.
However, I don't know how to handle the different types that would be stored in this class. Ideally, I should be able to store anything that has a string representation. I have a few ideas but none of them really seem right:
Store pointers to a superclass that defines a toString function or operator<<, like Java's Object. But this would require me to make wrappers for any primitives I want to monitor.
Something like boost::any or boost::spirit::hold_any. I think any needs to be type casted before I can print it... I guess I could try/catch casting to a bunch of different types, but that would be slow. hold_any requires defined stream operators, which would be perfect... but I can't get it to work with pointers.
Anyone have any ideas?
I found a solution somewhere else. I was pretty blown away, so might as well post it here for future reference. It looks something like this:
class Stringable
{
public:
virtual ~Stringable() {};
virtual std::string str() const = 0;
using Ptr = std::shared_ptr<Stringable>;
};
template <typename T>
class StringableRef : public Stringable
{
private:
T* _ptr;
public:
StringableRef(T& ref)
: _ptr(&ref) {}
virtual ~StringableRef() {}
virtual std::string str() const
{
std::ostringstream ss;
ss << *_ptr;
return ss.str();
}
};
class ValueMonitor
{
private:
static std::map<std::string, Stringable::Ptr> _values;
public:
ValueMonitor() {}
~ValueMonitor() {}
template <typename T>
static void watch(const std::string& label, T& ref)
{
_values[label] = std::make_shared<StringableRef<T>>(ref);
}
static void printAll()
{
for (const auto& valueItr : _values)
{
const String& name = valueItr.first;
const std::shared_ptr<Stringable>& value = valueItr.second;
std::cout << name << ": " << value->str() << std::endl;
}
}
static void clear()
{
_values.clear();
}
};
std::map<std::string, Stringable::Ptr> ValueMonitor::_values;
.
int main()
{
int i = 5;
std::string s = "test"
ValueMonitor::watch("number", i);
ValueMonitor::watch("string", s);
ValueMonitor::printAll();
i = 10;
s = "new string";
ValueMonitor::printAll();
return 0;
}

Derived class serialization without class tracking in Boost (C++)

I have some problems with boost serialization when serializing derived class through base class pointer. I need a system which serializes some objects as they are being received in the system, so I need to serialize over time. This is not really a problem since I can open a boost::archive::binary_oarchive and serialize objects when required. Rapidly I noticed that boost was performing object tracking by memory address, so the first problem was that different objects in time that share the same memory address were saved as the same object. This can be fixed by using the following macro in the required derived class:
BOOST_CLASS_TRACKING(className, boost::serialization::track_never)
This works fine, but again, when the base class is not abstract, the base class is not serialized properly. In the following example, the base class serialization method is only called once with the first object. In the following, boost assumes that this object has been serialized before although the object has different type.
#include <iostream>
#include <fstream>
#include <boost/serialization/export.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/archive/archive_exception.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
using namespace std;
class AClass{
public:
AClass(){}
virtual ~AClass(){}
private:
double a;
double b;
//virtual void virtualMethod() = 0;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & a;
ar & b;
cout << "A" << endl;
}
};
//BOOST_SERIALIZATION_ASSUME_ABSTRACT(Aclass)
//BOOST_CLASS_TRACKING(AClass, boost::serialization::track_never)
class BClass : public AClass{
public:
BClass(){}
virtual ~BClass(){}
private:
double c;
double d;
virtual void virtualMethod(){};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & boost::serialization::base_object<AClass>(*this);
ar & c;
ar & d;
cout << "B" << endl;
}
};
// define export to be able to serialize through base class pointer
BOOST_CLASS_EXPORT(BClass)
BOOST_CLASS_TRACKING(BClass, boost::serialization::track_never)
class CClass : public AClass{
public:
CClass(){}
virtual ~CClass(){}
private:
double c;
double d;
virtual void virtualMethod(){};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & boost::serialization::base_object<AClass>(*this);
ar & c;
ar & d;
cout << "C" << endl;
}
};
// define export to be able to serialize through base class pointer
BOOST_CLASS_EXPORT(CClass)
BOOST_CLASS_TRACKING(CClass, boost::serialization::track_never)
int main() {
cout << "Serializing...." << endl;
{
ofstream ofs("serialization.dat");
boost::archive::binary_oarchive oa(ofs);
for(int i=0;i<5;i++)
{
AClass* baseClassPointer = new BClass();
// serialize object through base pointer
oa << baseClassPointer;
// free the pointer so next allocation can reuse memory address
delete baseClassPointer;
}
for(int i=0;i<5;i++)
{
AClass* baseClassPointer = new CClass();
// serialize object through base pointer
oa << baseClassPointer;
// free the pointer so next allocation can reuse memory address
delete baseClassPointer;
}
}
getchar();
cout << "Deserializing..." << endl;
{
ifstream ifs("serialization.dat");
boost::archive::binary_iarchive ia(ifs);
try{
while(true){
AClass* a;
ia >> a;
delete a;
}
}catch(boost::archive::archive_exception const& e)
{
}
}
return 0;
}
When executing this piece of code, the result is as follow:
Serializing....
A
B
B
B
B
B
C
C
C
C
C
Deserializing...
A
B
B
B
B
B
C
C
C
C
C
So the base class is only being serialized once, although the derived class has explicitly the track_never flag. There are two different workarounds to fix this behaviour. The first one is to make the base class abstract with a pure virtual method and calling the macro BOOST_SERIALIZATION_ASSUME_ABSTRACT(Aclass), and the second one is to put the track_never flag also in the base class (commented in code).
None of these solutions meets my requirements, since I want to do in the future punctual serializations of the system state, which would require tracking features for a given DClass extending A (not B or C), and also the AClass should not be abstract.
Any hints? Is there any way to call explicitly the base class serialization method avoiding the tracking feature in the base class (that already has been disabled in the derived class)?
After having a little closer look to boost::serialization I'm also convinced there is no straightforward solution for you request.
As you already mentioned the tracking behavior for the serialization is declared on a class by class base with BOOST_CLASS_TRACKING.
This const global information is than interpret in the virtual method tracking from class oserializer.
virtual bool tracking(const unsigned int /* flags */)
Because this is a template class you can explicitly instantiate this method for your classes.
namespace boost {
namespace archive {
namespace detail {
template<>
virtual bool oserializer<class binary_oarchive, class AClass >::tracking(const unsigned int f /* flags */) const {
return do_your_own_tracking_decision();
}
}}}
Now you can try to e.g have something like a global variable and change the tracking behavior from time to time. (E.g depending on which derivate class is written to the archive.)
This seems to wok for “Serializing“ but the “Deserializing“ than throw an exception.
The reason for this is, that the state of “tracking” for each class is only written ones to the archive. Therefore the deserialize does always expect the data for AClass if BClass or CClass is read (at leased if the first write attempt for AClass was with tracking disabled).
One possible solution could be to use the flags parameter in tracking() method.
This parameter represent the flags the archive is created with, default “0”.
binary_oarchive(std::ostream & os, unsigned int flags = 0)
The archive flags are declared in basic_archive.hpp
enum archive_flags {
no_header = 1, // suppress archive header info
no_codecvt = 2, // suppress alteration of codecvt facet
no_xml_tag_checking = 4, // suppress checking of xml tags
no_tracking = 8, // suppress ALL tracking
flags_last = 8
};
no_tracking seems currently not to be supported, but you can now add this behavior to tracking.
template<>
virtual bool oserializer<class binary_oarchive, class AClass >::tracking(const unsigned int f /* flags */) const {
return !(f & no_tracking);
}
Now you can at leased decide for different archives whether AClass should be tracked or not.
boost::archive::binary_oarchive oa_nt(ofs, boost::archive::archive_flags::no_tracking);
And this are the changes in your example.
int main() {
cout << "Serializing...." << endl;
{
ofstream ofs("serialization1.dat");
boost::archive::binary_oarchive oa_nt(ofs, boost::archive::archive_flags::no_tracking);
//boost::archive::binary_oarchive oa(ofs);
for(int i=0;i<5;i++)
{
AClass* baseClassPointer = new BClass();
// serialize object through base pointer
oa_nt << baseClassPointer;
// free the pointer so next allocation can reuse memory address
delete baseClassPointer;
}
ofstream ofs2("serialization2.dat");
boost::archive::binary_oarchive oa(ofs2);
//boost::archive::binary_oarchive oa(ofs);
for(int i=0;i<5;i++)
{
AClass* baseClassPointer = new CClass();
// serialize object through base pointer
oa << baseClassPointer;
// free the pointer so next allocation can reuse memory address
delete baseClassPointer;
}
}
getchar();
cout << "Deserializing..." << endl;
{
ifstream ifs("serialization1.dat");
boost::archive::binary_iarchive ia(ifs);
try{
while(true){
AClass* a;
ia >> a;
delete a;
}
}catch(boost::archive::archive_exception const& e)
{
}
ifstream ifs2("serialization2.dat");
boost::archive::binary_iarchive ia2(ifs2);
try{
while(true){
AClass* a;
ia2 >> a;
delete a;
}
}catch(boost::archive::archive_exception const& e)
{
}
}
return 0;
}
namespace boost {
namespace archive {
namespace detail {
template<>
virtual bool oserializer<class binary_oarchive, class AClass >::tracking(const unsigned int f /* flags */) const {
return !(f & no_tracking);
}
}}}
This still may not be what you are looking for. There are lot more methods which could be adapted with an own implementation. Or your have to derivate your own archive class.
Ultimately the problem seems to be that a boost::serialization archive represents state at a single point in time, and you want your archive to contain state that has changed, i.e. pointers that have been reused. I don't think there is a simple boost::serialization flag that induces the behavior you want.
However, I think there are other workarounds that might be sufficient. You can encapsulate the serialization for a class into its own archive, and then archive the encapsulation. That is, you can implement the serialization for B like this (note that you have to split serialize() into save() and load()):
// #include <boost/serialization/split_member.hpp>
// #include <boost/serialization/string.hpp>
// Replace serialize() member function with this.
template<class Archive>
void save(Archive& ar, const unsigned int version) const {
// Serialize instance to a string (or other container).
// std::stringstream used here for simplicity. You can avoid
// some buffer copying with alternative stream classes that
// directly access an external container or iterator range.
std::ostringstream os;
boost::archive::binary_oarchive oa(os);
oa << boost::serialization::base_object<AClass>(*this);
oa << c;
oa << d;
// Archive string to top level.
const std::string s = os.str();
ar & s;
cout << "B" << endl;
}
template<class Archive>
void load(Archive& ar, const unsigned int version) {
// Unarchive string from top level.
std::string s;
ar & s;
// Deserialize instance from string.
std::istringstream is(s);
boost::archive::binary_iarchive ia(is);
ia >> boost::serialization::base_object<AClass>(*this);
ia >> c;
ia >> d;
cout << "B" << endl;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
Because each instance of B is serialized into its own archive, A is effectively not tracked because there is only one reference per archive of B. This produces:
Serializing....
A
B
A
B
A
B
A
B
A
B
A
C
C
C
C
C
Deserializing...
A
B
A
B
A
B
A
B
A
B
A
C
C
C
C
C
A potential objection to this technique is the storage overhead of encapsulation. The result of the original test program are 319 bytes while the modified test program produces 664 bytes. However, if gzip is applied to both output files then the sizes are 113 bytes for the original and 116 bytes for the modification. If space is a concern then I would recommend adding compression to the outer serialization, which can be easily done with boost::iostreams.
Another possible workaround is to extend the life of instances to the lifespan of the archive so pointers are not reused. You could do this by associating a container of shared_ptr instances to your archive, or by allocating instances from a memory pool.

writing list of dynamic array to file in binary form>

I want to write a structure which has a list of integer id. The list can be of varying length.
typedef struct ss_iidx_node {
int totalFreq;
vector < int > docIDList;
}s_iidx_node;
Now, I wish to write this structure in a file and read it back.
How can I do it?
Wrting is done:
fwrite(&obj,sizeof(s_iidx_node),1,dat_fd2);
When I read it back, it gives garbage value. It seems it storing only the strating and ending position of stl vector...which on reading is garbage?
Any idea how to do it
Thanks
Your code is simply non-portable. It tries to treat object as a raw sequence of bytes, which is plainly undefined for non-POD objects in the C++ standard (and your struct is non-POD because it contains a member of a non-POD type std::vector).
What happens in practice is that vector class typically consists of 3 fields: pointer to beginning of data, size, and capacity. What you see are bytes constituting those values written into the file.
You should consider avoiding C-style file I/O entirely, and using C++ streams and Boost Serialization library instead - it supports STL collections out of the box.
Though I'd rather see an approach based on an explicit serialisation, you could try:
fwrite(&obj.totalFreq,sizeof(int),1,dat_fd2);
fwrite(&obj.docIDList[0],sizeof(int),obj.totalFreq,dat_fd2);
Assuming totalFreq == docIDList.size(), it's a spurious variable, so a better implementation would be:
size_t size=obj.docIDList.size();
fwrite(&size,sizeof(size_t),1,dat_fd2);
fwrite(&obj.docIDList[0],sizeof(int),size,dat_fd2);
My preferred implementation would be:
size_t size=obj.docIDList.size();
fwrite(&size,sizeof(size_t),1,dat_fd2);
for (size_t i=0;i<size;i++)
{
int id=obj.docIDList[i];
fwrite(&id,sizeof(int),1,dat_fd2);
}
The vector class is defined roughly like this:
template <typename T>
class vector {
...
T* array; // pointer to the actual data, stored in a dynamically allocated array
size_t arrayLength;
...
};
The actual data of the vector are stored in a dynamically allocated array. The vector class simply holds a pointer to that array. So your fwrite call only stores the contents of the vector class, not the contents of the array it points to.
You need to write out the actual elements of the vector instead.
I tried this on VS2010 Beta1. Did not try on other compilers. Please check out.
class Employee
{
private:
int _empno;
string _name;
public:
Employee(int empno, string name) : _empno(empno), _name(name) { }
Employee() : _empno(-1), _name("") { }
virtual ~Employee() { }
virtual int GetEmpId() const;
virtual string GetName() const;
friend ostream& operator<<(ostream& os, const Employee& emp);
};
class Manager : public Employee
{
private:
vector<Employee> Reportees;
public:
Manager() : Employee() { }
Manager(int empno, const string& name) : Employee(empno, name) { }
~Manager() { }
void InsertEmployees(const Employee& emp);
friend ostream& operator<<(ostream& os, const Manager& manger);
};
void Manager::InsertEmployees(const Employee& emp)
{
Reportees.push_back(emp);
}
ostream& operator<<(ostream& os, const Manager& manager)
{
os << "Empid:" << manager.GetEmpId()
<< "|Name:" << manager.GetName() << endl;
typedef vector<Employee>::const_iterator EmpIter;
EmpIter iter = manager.Reportees.begin();<br>
for ( ; iter != manager.Reportees.end(); ++iter)
{
Employee e = *iter;
os << "Reportee" << endl;
os << "Empid:" << e.GetEmpId()
<< "|Name:" << e.GetName() << endl;
}
return os;
}
int main()
{
ofstream data("data.txt");
ofstream bin_data("data.bin", ios::binary);
Employee e1(100, "Jagan");
Employee e2(101, "Nath");
Employee e3(102, "Sai");
Employee e4(103, "Pantula");
Manager m(104, "Neeraja");
m.InsertEmployees(e1);
m.InsertEmployees(e2);
m.InsertEmployees(e3);
m.InsertEmployees(e4);
data << m;
data.close();
bin_data.write(reinterpret_cast<char*>(&m), sizeof(m));
bin_data.close();
ReadDataFromFile();
bin_data.close();
}
void ReadDataFromFile()
{
ifstream bin_data("data.bin", ios::binary);
Manager m;
while (bin_data.read(reinterpret_cast<char*>(&m), sizeof(m)))
cout << m;
}