Reputation: 971
I'm trying to learn how to serialize C++ objects.
After reading couple of posts here, it seems a nice idea to utilize boost
serialization functions and archiving using load/save functions. However, I would like to avoid using boost library.
Conceptually, can I save an object without knowing its fields. With no reflection in C++, is the only way to store an object is to know all its class members.
Can using stringstream
and overloading the <<
operator to convert an object to a string, can I directly save an object.
Thanks, K.
Upvotes: 0
Views: 533
Reputation: 4129
If you want to copy the object as it is like a true snapshot, we can read the bytes in the object one by one and store them as characters and paste them onto another object or save it in a file. This is a rough idea:
#include <iostream>
#include <iomanip>
struct Serialization
{
std::string data;
template <class T>
void in(const T &obj)
{
auto size = sizeof(T);
const char *ptr = (const char *)&obj;
for (size_t i = 0; i < size; i++)
data.push_back(*(ptr + i));
}
template <class T>
void out(T &obj)
{
char *ptr = (char *)&obj;
for (size_t i = 0; i < data.size(); i++)
*(ptr + i) = data[i];
}
void clear() { data.clear(); }
};
We don't know what is inside the object, it just gets copied as it is. This is somehow easy to use because shallow-copies any object, but also not desirable when
There are pointer family members (raw pointers, smart pointers, std::vector,...) that own some memory out of the object. Only pointers are copied but the target memories won't be copied.
It also copies a class's vtable pointer which I don't know if it would be useful at all.
Examples:
class person
{
int age;
char name[3];
float height;
public:
person() = default;
person(int a, char n[3], float h) : age{a}, height{h}
{
for (size_t i = 0; i < 3; i++)
name[i] = n[i];
}
void print()
{
std::cout << age << " " << name[0] << name[1] << name[2] << " " << height << "\n";
}
};
int main()
{
char x[3] = {'1', '2', '3'};
person m{12, x, 180};
person n;
Serialization s;
s.in(m);
s.out(n);
n.print(); // 12 123 180
s.clear();
double d;
s.in(1.2345678910);
s.out(d);
std::cout << std::setprecision(10) << d; // 1.234567891
return 0;
}
The Serialization class can be extended to serialize multiple identical/different objects by putting some conversions for writing strings, like repetitions of
object name size => object name => object data size => object data
Upvotes: 0
Reputation: 275350
An approach you can follow is to support tuple-likes and iterables by default.
Have a read_archive and write_archive that first does fancy sfinae to detect types that support for(:)
loops, and types that are tuple-like in that they support std::tuple_size
and ADL get<I>
.
This also lines up with one form of structured binding support in C++17.
Next, have a to_tie
sfinae adl enabled based implementation, that checks to_tie()
and to_tie(.)
. If so and read/writable, uses that.
Maybe somewhere in there include an adl lookup of an archive_io
function, so you can explicitly write your own io.
The goal is to auto-archive as much as possible, and what you cannot make it easy to compose with.
Upvotes: 0
Reputation: 1
Conceptually, can I save an object without knowing its fields.
No, you can't.
With no reflection in C++, is the only way to store an object is to know all its class members.
Yes. The best way is to leave that knowledge encapsulated in that class itself:
class MyClass {
public:
std::ostream& put(std::ostream& os) const {
os << field1 << " " << field2 << std::endl;
return os;
}
friend std::ostream& operator<<(std::ostream& os, const MyClass& myclass) {
return myClass.put(os);
}
private:
int field1;
double field2;
};
Upvotes: 1