How to unserialize a binary file into an object? - c++

I used this method to serialize my object :
void save(Obj& obj) {
ofstream os("obj.dat", ofstream::binary);
boost::archive::binary_oarchive ar(os, boost::archive::no_header);
ar << boost::serialization::make_binary_object(&obj, sizeof(obj));
}
What would be my the code for my Obj load(string fileName) ?

It's basically the same as what you had:
Obj load(std::string const& filename) {
std::ifstream is(filename, std::ios::binary);
boost::archive::binary_iarchive ar(is, boost::archive::no_header);
Obj obj;
ar >> boost::serialization::make_binary_object(&obj, sizeof(obj));
return obj;
}
Of course, this is assuming that your type is valid for use with make_binary_object: make sure that Obj is bitwise serializable (POD):
static_assert(std::is_pod<Obj>::value, "Obj is not POD");
Also, reconsider using namespace: Why is "using namespace std;" considered bad practice?

Related

Is there a way to make a smart pointer that points to either a std::ifstream or std::cin?

I want to take input from an istream from either a std::ifstream or std::cin depending on some condition.
As far as I can get it working, I had to use a raw std::istream* pointer:
int main(int argc, char const* argv[]) {
std::istream *in_stream;
std::ifstream file;
if (argc > 1) {
std::string filename = argv[1];
file.open(filename);
if (not file.is_open()) {
return -1;
}
in_stream = &file;
} else {
in_stream = &std::cin;
}
// do stuff with the input, regardless of the source...
}
Is there a way to rewrite the above using smart pointers?
This may be a question about ownership: How does one wrap and pass around two different std::istream instances, one of which is owned and needs disposal (which calls close() upon destruction) and ownership transfers, whereas the other one is not owned by user code (e.g. std::cin) and cannot be deleted. The solution is polymorphism: Create an interface like
struct StreamWrapper {
virtual std::istream &stream() = 0;
virtual ~StreamWrapper() = default;
};
and two implementations, e.g. OwnedStreamWrapper and UnownedStreamWrapper. The former can contain a std::istream directly (which is moveable, but not copyable) and the latter can contain (e.g.) a std::istream&, i.e. have no ownership over the referenced stream instance.
struct OwnedStreamWrapper : public StreamWrapper {
template <typename... A>
OwnedStreamWrapper(A &&...a) : stream_{std::forward<A>(a)...} {}
std::istream &stream() override { return stream_; }
private:
std::ifstream stream_;
};
struct UnownedStreamWrapper : public StreamWrapper {
UnownedStreamWrapper(std::istream &stream) : stream_{stream} {}
std::istream &stream() override { return stream_; }
private:
std::istream &stream_;
};
Apart from the stream ownership, wrapper ownership matters as well and can be used either to handle the wrappers’ (and streams’) lifespan automatically on the stack, or to allow a wrapper’s lifespan to exceed that of the current stack frame. In the example below, the first ReadWrapper() takes ownership of the wrapper and deletes it automatically whereas the second ReadWrapper() does not take ownership. In both cases polymorphism (i.e. the implementation of StreamWrapper) determines how the underlying stream is handled, i.e. whether it outlives its wrapper (like std::cin should) or dies together with it (like a manually instantiated stream should).
void ReadWrite(std::istream &in, std::ostream &out) {
std::array<char, 1024> buffer;
while (in) {
in.read(buffer.data(), buffer.size());
out.write(buffer.data(), in.gcount());
}
}
void ReadWrapper(std::unique_ptr<StreamWrapper> stream) {
ReadWrite(stream->stream(), std::cout);
}
void ReadWrapper(StreamWrapper &stream) {
ReadWrite(stream.stream(), std::cout);
}
Below are all four combinations of { owned | unowned } { streams | stream wrappers } in a runnable example:
#include <array>
#include <fstream>
#include <iostream>
#include <memory>
#include <utility>
namespace {
/********** The three snippets above go here! **********/
} // namespace
int main() {
OwnedStreamWrapper stream1{"/proc/cpuinfo"};
std::unique_ptr<StreamWrapper> stream2{
std::make_unique<OwnedStreamWrapper>("/proc/cpuinfo")};
std::ifstream stream3_file{"/proc/cpuinfo"}; // Let’s pretend it is unowned.
UnownedStreamWrapper stream3{stream3_file};
std::ifstream stream4_file{"/proc/cpuinfo"}; // Let’s pretend it is unowned.
std::unique_ptr<StreamWrapper> stream4{
std::make_unique<UnownedStreamWrapper>(stream4_file)};
ReadWrapper(stream1); // owned stream, wrapper kept
ReadWrapper(std::move(stream2)); // owned stream, wrapper transferred
ReadWrapper(stream3); // unowned stream, wrapper kept
ReadWrapper(std::move(stream4)); // unowned stream, wrapper transferred
}
A smart pointer is kind of an awkward fit for this situation, because it requires a dynamic allocation (which is probably not necessary in this case)
1. Do your processing in another function
As #BoP pointed out in the comments a nice way to handle this could be to use another function that gets passed the appropriate input stream:
godbolt
int do_thing(std::istream& in_stream) {
// do stuff with the input, regardless of the source...
int foobar;
in_stream >> foobar;
return 0;
}
int main(int argc, char const* argv[]) {
std::ofstream{"/tmp/foobar"} << 1234;
if(argc > 1) {
std::ifstream file{argv[1]};
if(!file) return -1;
return do_thing(file);
} else {
return do_thing(std::cin);
}
}
2. Switch the stream buffer
If you won't be using std::cin at all if a filename is passed you could change the streambuffer of std::cin to the one of the file with rdbuf() - that way you can use std::cin in both cases.
godbolt
std::ifstream file;
std::streambuf* oldbuf = std::cin.rdbuf();
if (argc > 1) {
file.open(argv[1]);
if (!file) return -1;
// switch buffer of std::cin
std::cin.rdbuf(file.rdbuf());
}
// do stuff with the input, regardless of the source...
int foobar;
std::cin >> foobar; // this will reader either from std::cin or the file, depending on the current streambuffer assigned to std::cin
// restore old stream buffer
std::cin.rdbuf(oldbuf);
3. Using std::unique_ptr / std::shared_ptr with a custom deleter
The easiest solution if you still want to use smart pointers would be to use std::unique_ptr / std::shared_ptr with a custom deleter that handles the std::cin case:
godbolt
// deletes the stream only if it is not std::cin
struct istream_ptr_deleter {
void operator()(std::istream* stream) {
if(&std::cin == stream) return;
delete stream;
}
};
using istream_ptr = std::unique_ptr<std::istream, istream_ptr_deleter>;
// Example Usage:
istream_ptr in_stream;
if (argc > 1) {
in_stream.reset(new std::ifstream(argv[1]));
if (!*in_stream) return -1;
} else {
in_stream.reset(&std::cin);
}
int foobar;
*in_stream >> foobar;

Saving and retrieving multiple objects using boost serialization

How can I use boost serialization to save and get multiple objects (number of objects varies)?
For example I have class Contact, and I input contact and save it to file. Another time I input another contact, and it should also be saved in file.
I think the save function should be like this:
void save_contact(const Contact &s, const char * filename){
std::ofstream ofs(filename, std::ios::app);
boost::archive::text_oarchive oa(ofs);
oa << s;
ofs << "\n";
}
And to retrieve the contacts I should keep track of contacts number, am I right?
To retrieve single contact I use the following code:
void retrieve_contact(Contact &s, const char * filename)
{
std::ifstream ifs(filename);
boost::archive::text_iarchive ia(ifs);
ia >> s;
}
Here is how serialization function inside Contact class looks like:
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & m_CompanyName;
ar & m_FirstName;
ar & m_LastName;
ar & m_PhoneNumbers;
}
(m_CompanyName, m_FirstName and m_LastName are std::string, m_PhoneNumbers is std::vector)
So is there way somehow to retrieve contacts without keeping track of number of contacts? Or can you suggest me another way to save and retrieve contacts, saved at different time? Also how can I edit the saved file to modify Contact?
Yeah, use the predefined serialization for standard containers. So, e.g.
#include <boost/serialization/vector.hpp>
And then
void retrieve_contact(std::vector<Contact>& s, const char * filename)
{
std::ifstream ifs(filename);
boost::archive::text_iarchive ia(ifs);
ia >> s;
}

Class member initializer to initialize ifstream with error check?

I am trying to use C++11 feature of class member initializer to initialize variables of class. The variables of class that I have are std::string and std::ifstream.
class A{
std::string filename = "f1.txt";
std::ifstream filestream = ....
public:
....
};
Is there any way to initialize filestream and also check for error at the same time using class member initialization.
What I want to do is, something similar to below :
class A{
std::string filename = "f1.txt";
std::ifstream filestream(filename);
if(filestream.is_open()) .... // check if file cannot be opened
public:
....
};
You can write and call an inline lambda-expression that performs the appropriate checks; such a lambda-expression has access to the data members:
class A {
std::string filename = "f1.txt";
std::ifstream filestream = [&] {
std::ifstream fs{filename};
if (!fs)
throw std::runtime_error("failed to open ifstream");
return fs;
}();
};
It may be clearer to separate out the logic into a reusable helper function taking filename as a parameter, e.g. a static member function:
class A {
std::string filename = "f1.txt";
std::ifstream filestream = openChecked(filename);
static std::ifstream openChecked(std::string const& filename)
{
std::ifstream fs{filename};
if (!fs)
throw std::runtime_error("failed to open ifstream");
return fs;
}
};

ifstream variable in class

I have my class which has to have ifstream file in it.
I dont know how to present it in the class header
A:
class MyClass
{
...
ifstream file;
...
}
B:
class MyClass
{
...
ifstream& file;
...
}
I know that ifstream has to get path in the decaleration, so how do I do it?
Also how do I open a file with it?
EDIT:
I want the first way, but how do I use it SYNTAX-ly?
let's say this is the header(part of it)
class MyClass
{
string path;
ifstream file;
public:
MyClass();
void read_from_file();
bool is_file_open();
...
}
funcs
void MyClass::read_from_file()
{
//what do I do to open it???
this->file.open(this->path); //Maybe, IDK
... // ?
}
You more than likely want the first option. The second is a reference to some other ifstream object, rather than an ifstream that belongs to MyClass.
You don't need to give an ifstream a path immediately. You can later call the ifstream's open function and give that a path. However, if you want to open the ifstream immediately on initialisation, you need to use the constructor's initialisation list:
MyClass() : file("filename") { }
If you need the constructor to take the file name, simply do:
MyClass(std::string filename) : file(filename) { }
Initialise it in the constructor:
class my_class {
public:
my_class(char const* path) : file(path) {
}
my_class(std::string const& path) : my_class(path.c_str()) {
}
private:
std::ifstream file;
};
Also see The Definitive C++ Book Guide and List.

Working with file streams generically

I want to work with file streams generically. That is, i want to 'program to an interface and not the implementation'. Something like this:
ios * genericFileIO = new ifstream("src.txt");
getline(genericFileIO, someStringObject);//from the string library; dont want to use C strings
genericFileIO = new ofstream("dest.txt");
genericFileIO -> operator<<(someStringObject);
Is it possible? I am not great with inheritance. Given the io class hierarchy, how do i implement what i want?
Do you mean:
void
pass_a_line(std::istream& in, std::ostream& out)
{
// error handling left as an exercise
std::string line;
std::getline(in, line);
out << line;
}
This can work with anything that is an std::istream and std::ostream, like so:
// from a file to cout
// no need to new
std::ifstream in("src.txt");
pass_a_line(in, std::cout);
// from a stringstream to a file
std::istringstream stream("Hi");
std::ofstream out("dest.txt");
pass_a_line(stream, out);
This does what your example do, and is programmed against the std::istream and std::ostream interfaces. But that's not generic programming; that's object oriented programming.
Boost.Iostreams can adapt classes to std::[i|o|io]streams, and does this using generic programming.
You can use different specialisations of the ostream or istream concepts over the ostream or istream interface.
void Write(std::ostream& os, const std::string& s)
{
os << "Write: " << s;
}
std::string Read(std::istream& is)
{
std::string s;
is >> s;
return s;
}
int main()
{
Write(std::cout, "Hello World");
std::ofstream ofs("file.txt");
if (ofs.good())
Write(ofs, "Hello World");
std::stringstream ss;
Write(ss, "StringStream");
Write(std::cout, ss.str());
std::string s = Read(std::cin);
Write(std::cout, s);
return 0;
}