Serializing generic C++ objects is a complex task in and of itself. This section describes some of the more advanced features of RCF's internal serialization framework.
RCF will automatically detect and serialize polymorphic pointers and references, as fully derived types. However, to do this, RCF needs to be configured with two pieces of information about the polymorphic type being serialized. First, RCF needs a runtime identifier string for the derived type. Second, it needs to know which base types the derived type will be serialized through.
Here is an example of a polymorphic class hierarchy, with associated serialization functions:
SF::serializeParent() is used to invoke base class serialization code. If you try to serialize the parent class directly, e.g. by calling
ar & static_cast<A&>(*this), RCF will detect that the parent class is actually a derived class, and will try to serialize the derived class once again.
Now consider the following RCF interface, which utilizes a class
X containing polymorphic
In order to polymorphically serialize
X::mAPtr, we need to do two things:
SF::registerType()to set runtime identifiers for the
Cclasses. The runtime identifiers will be included in serialized archives when
Cobjects are serialized, and will allow the deserialization code to construct objects of the appropriate type.
SF::registerBaseAndDerived()to specify which base classes the derived classes will be serialized through.
In our case, the following code is sufficient:
This code needs to be executed at the startup of both the client and the server. Once executed, we can make remote calls to the
Echo() method and the polymorphic
A pointers will be serialized and deserialized, as fully derived types.
If you serialize a pointer to the same object twice, RCF will by default serialize the entire object twice. This means that when the pointers are deserialized, they will point to two distinct objects. In most applications this is usually not an issue. However, some applications may want the deserialization code to instead create two pointers to the same object.
RCF supports this through a pointer tracking concept, where an object is serialized in its entirety, only once, regardless of how many pointers to it are serialized. Upon deserialization, only a single object is created, and multiple pointers can then be deserialized, pointing to the same object.
To demonstrate pointer tracking, here is an an
I_Echo interface with an
Echo() function that takes a pair of
Here is client-side code to call
If we make a call to
Echo() with a pair of
shared_ptr<> objects pointing to the same
std::string, we'll find that the returned pair points to two distinct
std::string objects. To get them to point to the same
std::string, we can enable pointer tracking, on the client-side:
, and also on the server-side:
The two returned
shared_ptr<> objects will now point to the same instance.
Pointer tracking is a relatively expensive feature and should only be enabled if your application requires it. Pointer tracking requires the serialization framework to track all pointers and values that are being serialized to and from an archive, resulting in significant performance overhead.
Serialization and deserialization tend to be performed symmetrically, in the sense that serialization code writes a type
T to an archive, and deserialization code reads the same type
T from the archive.
Symmetry is not a requirement for serialization, however. If two classes
B have serialization functions that serialize the same number and type of members, then you can serialize an
A instance to an archive and deserialize a
B instance from the archive. Essentially, the classes
B are interchangeable from a serialization point of view.
RCF guarantees serialization interchangeability for a number of types. For example, for any type
T * are interchangeable. So you can serialize a pointer, and then deserialize it as a value:
Furthermore, smart pointers are interchangeable with native pointers, so you can serialize a
std::shared_ptr<T> and then deserialize it into a value
T. Here is another example:
A significant consequence of this is that if you start out using
std::unique_ptr<T> or some other smart pointer in your RCF interface, you can always change it at a later point in time, without breaking backward compatibility.
The following are classes of types that RCF guarantees to be interchangeable:
std::wstring objects presents some portability issues, as different platforms have different definitions of
wchar_t and different Unicode encodings for
RCF resolves this by converting
std::wstring objects to UTF-8 before serializing.
wchar_t, RCF assumes that any
std::wstringpassed to it, is encoded in UTF-16, and converts between UTF-16 and UTF-8.
wchar_t, RCF assumes that any
std::wstringpassed to it is encoded in UTF-32, and converts between UTF-32 and UTF-8.
If your application is sending or receiving
std::wstring objects, encoded in something other than the assumed UTF-16 or UTF-32 encodings, you should be aware that cross-platform portability may be affected.