PrevUpHomeNext

Third Party Serialization

Boost.Serialization
Protocol Buffers

RCF provides support for several third pary serialization frameworks.

Boost.Serialization is a library conceptually similar to RCF's internal serialization framework, providing generic serialization for C++ classes. For reference material and FAQ's on Boost.Serialization itself, please refer to Boost documentation and the Boost mailing lists.

To utilize Boost.Serialization within RCF, you will need to define RCF_USE_BOOST_SERIALIZATION when building RCF, and then provide Boost.Serialization serialize() functions for all your datatypes.

RCF supports two archive types from Boost.Serialization - binary archives and text archives. To specify which one you want to use, call ClientStub::setSerializationProtocol():

RcfClient<I_X> client( RCF::TcpEndpoint(50001) );

// To use Boost.Serialization with binary archives.
client.getClientStub().setSerializationProtocol(RCF::Sp_BsBinary);

// To use Boost.Serialization with text archives.
client.getClientStub().setSerializationProtocol(RCF::Sp_BsText);

If RCF has also been built with RCF_USE_SF_SERIALIZATION defined, you can also specify SF serialization:

// To use SF serialization.
client.getClientStub().setSerializationProtocol(RCF::Sp_SfBinary);

Given the choice between using SF and using Boost.Serialization, which should you use? The answer of course depends largely on the needs of your own application. However, within the context of RCF, we feel SF is a better choice for a number of reasons:

  • SF is RCF's native serialization implementation. It is updated along with RCF, and tested against RCF. It's behavior and configuration are not dependent on any particular version of Boost.
  • SF has a robust internal versioning scheme. The SF runtime can adjusts its archive formats to be compatible with any older version of SF. This capability is crucial for implementing backwards compatibility in distributed applications, and is lacking in Boost.Serialization.
  • SF is optimized for IPC usage, where tens of thousands of archives may be created and destroyed every second.
  • SF intentionally makes it easy to modify the types in your application, so that newer versions of an application can evolve their object models, without affecting wire compatibility with older versions of the same application (see Interchangeable types ). In contrast, Boost.Serialization requires exact matches on source and destination types.
  • SF has a robust type registration system. Type registration with Boost.Serialization involves macros, global static objects, and implicit code generation, with application wide side-effects. In some versions of Boost, type registration even requires including headers in a particular order. In contrast, type registration with SF is as simple as calling SF::registerType() and SF::registerBaseAndDerived().
  • SF archives are binary and guaranteed to be portable across platforms. Boost.Serialization binary archives are not portable, and hence can't be used for cross-platform IPC. Boost.Serialization text archives are portable, but at the cost of being much slower than binary archives.
  • SF serialization functions are not templated, which means their definitions can be moved to source files. Boost.Serialization serialization functions are generally templated on the archive type, thus requiring their implementations to be placed inline in header files, causing excessive coupling and longer compile times.
  • Boost.Serialization archive formats can change implicitly, depending on global static code instantiations in unrelated parts of the same executable, to the point where the archives produced cannot be loaded again (see this bug report, which unfortunately was closed without a fix).

Protocol Buffers is an open source project released by Google, which uses a specialized language to define serialization schemas, from which actual serialization code is generated. The official Protocol Buffers compiler can generate code in three languages (C++, Java and Python), and various third party projects are available to provide support for other languages.

RCF provides native marshaling support for classes generated by the Protocol Buffers compiler. To enable this support, define RCF_USE_PROTOBUF when building RCF. With RCF_USE_PROTOBUF defined, classes produced by the Protocol Buffers compiler, can be used directly in RCF interface declarations, and will be serialized and deserialized using the Protocol Buffer runtime.

For more information on building RCF with Protocol Buffer support, see Appendix - Building.

As an example, consider this .proto file.

// Person.proto

message Person {
  required int32 id = 1;
  required string name = 2;
  optional string email = 3;
}

Running the Protocol Buffer compiler (in C++ mode) over Person.proto results in the two files Person.pb.h and Person.pb.cc, containing the implementation of the C++ class Person.

// Person.pb.h

class Person : public ::google::protobuf::Message {
// ...
}

To use the class Person in an RCF interface, simply include Person.pb.h . RCF will detect that the Person class is a Protocol Buffer class, and will use Protocol Buffer functions to serialize and deserialize Person instances.

#include <RCF/../../test/protobuf/messages/cpp/Person.pb.h>


RCF_BEGIN(I_X, "I_X")
    RCF_METHOD_R1(Person, echo, const Person &)
RCF_END(I_X)

Protocol Buffer classes can be intermingled with native C++ classes, in RCF interfaces:

RCF_BEGIN(I_X, "I_X")
    RCF_METHOD_R1(Person, echo, const Person &)
    RCF_METHOD_V4(void , func, int, const std::vector<std::string> &, Person &, RCF::ByteBuffer)
RCF_END(I_X)

The serialization and deserialization code generated by Protocol Buffers is highly optimized. However, to make the most of this performance, you may want to cache and reuse Protocol Buffer objects from call to call, rather than creating new ones. Protocol Buffer objects will hold on to any memory they allocate, and will reuse the memory in subsequent serialization and deserialization operations.

RCF provides server-side object caching, for this purpose. For example, to enable server-side caching of the Person class:

RCF::ObjectPool & cache = RCF::getObjectPool();

// Enable server-side caching for Person.
// * Don't cache more than 10 Person objects.
// * Call Person::Clear() before putting a Person object into the cache.
cache.enableCaching<Person>(10, boost::bind(&Person::Clear, _1));


PrevUpHomeNext