Serialization is a fundamental part of every remote call. Remote call arguments need to be serialized into a binary format so they can be transmitted across the network, and once received, they need to be deserialized from a binary format back into regular C++ objects.
RCF provides a built-in serialization framework which handles serialization of common C++ types automatically, and can be customized to serialize arbitrary user-defined C++ types.
Serialization of fundamental C++ types (
double, etc) is handled automatically by RCF. Serialization of a number of other common and standard C++ types requires inclusion of the relevant header file:
|Type||Serialization header to include|
C++ enums are serialized automatically, as integers. Serialization of C++11 enum classes requires the use of a helper macro:
If you take a class of your own, and use it in an RCF interface:
, you'll end up with an compiler error similar to this one:
The compiler is telling us that it couldn't find any serialization code for the class
Point3D. We need to provide a
serialize() function, either as a member function:
, or as a free function in either the same namespace as
Point3D, or in the
The code in the
serialize() function specifies which members to serialize.
serialize() function is used both for serialization and deserialization. In some cases, you may want to use different logic, depending on whether the code is serializing or deserializing an object. For example, the following snippet implements serialization of the
boost::gregorian::date class, by representing it as a string:
To send a chunk of binary data, you can use the
std::vector<char> could be used for the same purpose. However, serialization and marshaling of
RCF::ByteBuffer is significantly more efficient (see Performance). With
RCF::ByteBuffer, no copies at all will be made of the data, on either end of the wire.
Because C++ does not prescribe the sizes of its fundamental types, there is potential for serialization errors when servers and clients are deployed on different platforms. For example, consider the following interface:
Echo() method uses
std::size_t as a parameter and return value.
std::size_t has different meanings on different platforms. For example, the 32 bit Visual C++ compiler considers
std::size_t to be a 32 bit type, while the 64 bit Visual C++ compiler considers
std::size_t to be a 64 bit type. Consequently, if a remote call is made from a 32 bit client to a 64 bit server, using a method with
std::size_t parameters, a runtime serialization error will be raised.
The same issue arises when using the type
long with 32 and 64 bit versions of gcc. 32-bit gcc considers
long to be a 32 bit type, while 64-bit gcc considers
long to be a 64-bit type.
The correct approach in these situations is to use typedefs for arithmetic types whose bit sizes are guaranteed. In particular, the standard C++
<cstdint> header provides a number of useful typedefs, including the following.
|16 bit signed integer|
|16 bit unsigned integer|
|32 bit signed integer|
|32 bit unsigned integer|
|64 bit signed integer|
|64 bit unsigned integer|
To ensure portability, the example with
std::size_t, above, should be rewritten as:
For more advanced topics on serialization, see Advanced Serialization.