Sending and returning custom classes
We have seen previously that all STL containers can be
serialized and deserialized by Thallium. Thallium can also
serialize and deserialize user-defined classes, with a little help.
This help comes in the form of a serialize
function template
put inside the class.
3D point example
Here is an example with a point class representing a 3d point.
class point {
private:
double x;
double y;
double z;
public:
point(double a=0.0, double b=0.0, double c=0.0)
: x(a), y(b), z(c) {}
template<typename A>
void serialize(A& ar) {
ar & x;
ar & y;
ar & z;
}
};
You will also need the class to be default-constructible.
The template parameter (A) and the function’s parameter (ar)
represent an archive, from which one can serialize/deserialize data.
Note that you don’t need to provide two separate functions:
Thallium is smart enough to use the same serialize
function for
both reading and writing, depending on the context.
The serialize
function calls the operator &
of
the archive to either write or read the class’ data members.
Provided that those data members are basic types, user-defined
types with a serialize
method, or STL containers of a
serializable type, Thallium will know how to serialize the class.
Important
Thallium needs classes used as RPC argument/response to be default-constructible (i.e. provide a constructor that takes no argument).
Asymmetric serialization
In some cases it may not be possible for the serialize
function to present a symmetric behavior for reading and for
writing. For instance this may happen if the deserialization
function needs to allocate a pointer. In these cases, one can,
instead of a serialize
method, provide a save
method and a load
method. save
will be used
for serialization, load
will be used for deserialization.
Reading/writing raw data
It may also be convenient to read or write raw data from/to
the archive. For this, note that archive objects used for
serialization provide a write
method:
template<typename T> write(T* const t, std::size_t count=1)
and archive objects used for deserialization provide a read
method:
template<typename T> read(T* t, std::size_t count=1)
The write
method should be used only in a save
template
method, while the read
method should be used only in a load
template method.
Non-member serialization functions
In some contexts it may not be possible to add member functions to a class.
In those cases, you can add serialize
or load
/save
functions
outside of the class, as follows:
template<typename A>
void serialize(A& ar, MyType& x) {
...
}
or
template<typename A>
void save(A& ar, const MyType& x) {
...
}
template<typename A>
void load(A& ar, MyType& x) {
...
}
Type checking
Contrary to Margo, where types (structures) and their serialization function
will generally be defined in a header file shared by the client and the server,
Thallium’s template based serialization leaves the user open to the risk of
a type mismatch between the client and the server. During development, we
recommand to build your code with -DTHALLIUM_DEBUG_RPC_TYPES
, or if you
use cmake, to link your binaries/libraries against the thallium_check_types
target using target_link_libraries (mylib PRIVATE thallium_check_types)
.
This flag will make thallium add the name of the type it is serializing to the
serialization buffer. This adds an overhead but allows the receiving end to check
that the name of the types match, and to print an error if they don’t.