Serialization in Ignite.C++
BinaryType Templates
Most user-defined classes going through Ignite C++ API will be passed over the wire to other cluster nodes. These classes include your data records, compute tasks, and other objects.
Passing objects of these classes over wire requires serialization. For Ignite C++ it can be achieved by providing
a BinaryType
class template for your object type:
class Address
{
friend struct ignite::binary::BinaryType<Address>;
public:
Address() { }
Address(const std::string& street, int32_t zip) :
street(street), zip(zip) { }
const std::string& GetStreet() const
{
return street;
}
int32_t GetZip() const
{
return zip;
}
private:
std::string street;
int32_t zip;
};
template<>
struct ignite::binary::BinaryType<Address>
{
static int32_t GetTypeId()
{
return GetBinaryStringHashCode("Address");
}
static void GetTypeName(std::string& name)
{
name = "Address";
}
static int32_t GetFieldId(const char* name)
{
return GetBinaryStringHashCode(name);
}
static bool IsNull(const Address& obj)
{
return obj.GetZip() && !obj.GetStreet().empty();
}
static void GetNull(Address& dst)
{
dst = Address();
}
static void Write(BinaryWriter& writer, const Address& obj)
{
writer.WriteString("street", obj.GetStreet());
writer.WriteInt32("zip", obj.GetZip());
}
static void Read(BinaryReader& reader, Address& dst)
{
dst.street = reader.ReadString("street");
dst.zip = reader.ReadInt32("zip");
}
};
Also, you can use the raw serialization mode without storing names of the object’s fields in the serialized form. This mode is more compact and performs faster, but disables SQL queries that require to keep the names of the fields in the serialized form:
template<>
struct ignite::binary::BinaryType<Address>
{
static int32_t GetTypeId()
{
return GetBinaryStringHashCode("Address");
}
static void GetTypeName(std::string& name)
{
name = "Address";
}
static int32_t GetFieldId(const char* name)
{
return GetBinaryStringHashCode(name);
}
static bool IsNull(const Address& obj)
{
return false;
}
static void GetNull(Address& dst)
{
dst = Address();
}
static void Write(BinaryWriter& writer, const Address& obj)
{
BinaryRawWriter rawWriter = writer.RawWriter();
rawWriter.WriteString(obj.GetStreet());
rawWriter.WriteInt32(obj.GetZip());
}
static void Read(BinaryReader& reader, Address& dst)
{
BinaryRawReader rawReader = reader.RawReader();
dst.street = rawReader.ReadString();
dst.zip = rawReader.ReadInt32();
}
};
Serialization Macros
Ignite C++ defines a set of utility macros that could be used to simplify the BinaryType
specialization. Here is a list of such macros with description:
-
IGNITE_BINARY_TYPE_START(T)
- Start the binary type’s specialization. -
IGNITE_BINARY_TYPE_END
- End the binary type’s specialization. -
IGNITE_BINARY_GET_TYPE_ID_AS_CONST(id)
- Implementation ofGetTypeId()
which returns predefined constantid
. -
IGNITE_BINARY_GET_TYPE_ID_AS_HASH(T)
- Implementation ofGetTypeId()
which returns hash of passed type name. -
IGNITE_BINARY_GET_TYPE_NAME_AS_IS(T)
- Implementation ofGetTypeName()
which returns type name as is. -
IGNITE_BINARY_GET_FIELD_ID_AS_HASH
- Default implementation ofGetFieldId()
function which returns Java-way hash code of the string. -
IGNITE_BINARY_IS_NULL_FALSE(T)
- Implementation ofIsNull()
function which always returnsfalse
. -
IGNITE_BINARY_IS_NULL_IF_NULLPTR(T)
- Implementation ofIsNull()
function which returntrue
if passed object is null pointer. -
IGNITE_BINARY_GET_NULL_DEFAULT_CTOR(T)
- Implementation ofGetNull()
function which returns an instance created with default constructor. -
IGNITE_BINARY_GET_NULL_NULLPTR(T)
- Implementation of GetNull() function which returnsNULL
pointer.
You can describe the Address
class declared earlier using these macros:
namespace ignite
{
namespace binary
{
IGNITE_BINARY_TYPE_START(Address)
IGNITE_BINARY_GET_TYPE_ID_AS_HASH(Address)
IGNITE_BINARY_GET_TYPE_NAME_AS_IS(Address)
IGNITE_BINARY_GET_NULL_DEFAULT_CTOR(Address)
IGNITE_BINARY_GET_FIELD_ID_AS_HASH
static bool IsNull(const Address& obj)
{
return obj.GetZip() == 0 && !obj.GetStreet().empty();
}
static void Write(BinaryWriter& writer, const Address& obj)
{
writer.WriteString("street", obj.GetStreet());
writer.WriteInt32("zip", obj.GetZip());
}
static void Read(BinaryReader& reader, Address& dst)
{
dst.street = reader.ReadString("street");
dst.zip = reader.ReadInt32("zip");
}
IGNITE_BINARY_TYPE_END
}
}
Reading and Writing Values
There are several ways for writing and reading data. The first way is to use an object’s value directly:
CustomType val;
// some application code here
// ...
writer.WriteObject<CustomType>("field_name", val);
CustomType val = reader.ReadObject<CustomType>("field_name");
The second approach does the same but uses a pointer to the object:
// Writing null to as a value for integer field.
writer.WriteObject<int32_t*>("int_field_name", nullptr);
// Writing a value of the custom type by pointer.
CustomType *val;
// some application code here
// ...
writer.WriteObject<CustomType*>("field_name", val);
// Reading value which can be null.
CustomType* nullableVal = reader.ReadObject<CustomType*>("field_name");
if (nullableVal) {
// ...
}
// You can use a smart pointer as well.
std::unique_ptr<CustomType> nullablePtr = reader.ReadObject<CustomType*>();
if (nullablePtr) {
// ...
}
An advantage of the pointer-based technique is that it allows writing or reading null
values.
Apache, Apache Ignite, the Apache feather and the Apache Ignite logo are either registered trademarks or trademarks of The Apache Software Foundation.