Mercury Serialization Macros
For convenience, Mercury provides macros that can reduce the amount of code required to send an RPC call. Instead of using tedious RPC stubs and code generators, Mercury makes use of the Boost preprocessor library so that users can generate all the boilerplate code that is necessary to serialize and deserialize function arguments.
RPC Registration
When registering a new RPC through the RPC layer (see
previous section), users are expected to tell Mercury
how to serialize and deserialize input and output arguments. To facilitate that
and in conjunction with the following macros, the MERCURY_REGISTER()
macro
makes the registration
of RPC calls more convenient by mapping the types to the generated proc functions.
MERCURY_REGISTER(hg_class, func_name, in_struct_type_name, out_struct_type_name, rpc_cb);
Example
int rpc_open(const char *path, rpc_handle_t handle, int *event_id);
One can use the MERCURY_REGISTER
macro and pass the types of the input/output
structures directly. In cases where no input or no output argument is present,
the void
type can be passed to the macro.
rpc_id = MERCURY_REGISTER(hg_class, "rpc_open", rpc_open_in_t, rpc_open_out_t, rpc_open_cb);
Predefined Types
Mercury already defines some types and uses standard types so that the size of the type is fixed between platforms when serializing and deserializing it. For convenience, HG types can also be used to serialize bulk handles for example, but also strings, etc.
Predefined Types | Type name |
---|---|
Standard types | int8_t , uint8_t int16_t , uint16_t int32_t , uint32_t int64_t , uint64_t |
Strings | hg_string_t , hg_const_string_t |
Bulk descriptor | hg_bulk_t |
Mercury types | hg_bool_t , hg_id_t , hg_size_t , hg_ptr_t |
New Type Description
The macro MERCURY_GEN_PROC()
can be used to describe new types that are
generally composed of primitive types. The macro generates both a
new struct and a proc function that can be used
to serialize arguments. The structure fields contain either input arguments or
output arguments. The generated proc routine uses the proc routines from
pre-existing types to serialize and deserialize each of the fields.
MERCURY_GEN_PROC(struct_type_name, fields)
Example
The following function has two input arguments, one output argument and one return value.
int rpc_open(const char *path, rpc_handle_t handle, int *event_id);
The following macro can be used to generate boilerplate code for the input argument (again, refer to the predefined types section for a list of the pre-existing types that can be passed to this macro):
MERCURY_GEN_PROC( rpc_open_in_t, ((hg_const_string_t)(path))
((rpc_handle_t)(handle)) )
/* Will generate an rpc_open_in_t struct */
typedef struct {
hg_const_string_t path;
rpc_handle_t handle;
} rpc_open_in_t;
/* and an hg_proc_rpc_open_in_t proc function */
hg_return_t
hg_proc_rpc_open_in_t(hg_proc_t proc, void *data)
{
hg_return_t ret;
rpc_open_in_t *struct_data = (rpc_open_in_t *) data;
ret = hg_proc_hg_const_string_t(proc, &struct_data->path);
if (ret != HG_SUCCESS) {
/* error */
}
ret = hg_proc_rpc_handle_t(proc, &struct_data->handle);
if (ret != HG_SUCCESS) {
/* error */
}
return ret;
}
Note the parentheses that separate the name of the field and its type. Each field is then separated by another pair of parentheses. This follows the sequence data type of the Boost preprocessor library.
Existing struct
Description
In some cases, however, the argument types are not known by Mercury, which is
the case of the previous example with the rpc_handle_t
type. For these cases,
another macro, called MERCURY_GEN_STRUCT_PROC
, can be used. It defines a
serialization function for an existing struct or type—this assumes that the
type can be mapped to already existing types; if not, users can create their
own proc function and use the hg_proc_raw
routine that takes a stream of bytes.
MERCURY_GEN_STRUCT_PROC(struct_type_name, fields)
Example
The following function has one non-standard type, rpc_handle_t
.
int rpc_open(const char *path, rpc_handle_t handle, int *event_id);
/* pre-defined struct */
typedef struct {
hg_uint64_t cookie;
} rpc_handle_t;
The following macro can then be used to generate boilerplate code for the type by defining its fields.
MERCURY_GEN_STRUCT_PROC( rpc_handle_t, ((hg_uint64_t)(cookie)) )
/* Will generate an hg_proc_rpc_handle_t function */
static hg_return_t
hg_proc_rpc_handle_t(hg_proc_t proc, void *data)
{
hg_return_t ret;
rpc_handle_t *struct_data = (rpc_handle_t *) data;
ret = hg_proc_hg_uint64_t(proc, &struct_data->cookie);
if (ret != HG_SUCCESS) {
/* error */
}
return ret;
}