Skip to content

Choose a deserialization mode

Read the same bytes three ways and pick the one that matches what you need from them.

Source: example/howto/deserialization_modes.cpp, CMake target howto_deserialization_modes.

// Big-endian on the wire, so every integer is byte-swapped on a little-endian host: a conversion per field
struct [[=rbe::pack_be]] Trade {
  std::uint32_t trade_id;
  std::uint32_t quantity;
  std::uint64_t price;
  std::uint64_t timestamp;
};

// No annotations: the wire layout is the in-memory layout, so there is nothing to convert
struct Tick {
  std::uint64_t timestamp;
  std::uint32_t price;
  std::uint32_t quantity;
};

static_assert(not rbe::trivially_wirable<Trade>);
static_assert(rbe::trivially_wirable<Tick>);

Eager: the whole value, now

Every field is decoded and the result is an independent object.

// Every field is decoded now. The result owns its data: the buffer can be reused or freed afterwards.
Trade trade = rbe::deserialize<Trade>(bytes, rbe::dsrl::eager);

Pick it when you need the whole value, or when the buffer will be reused before you are done with it.

Lazy: a view, decoded on access

Nothing is read until you ask for a field. The view borrows the buffer, which must stay alive.

// Nothing is decoded until a field is read. The view borrows the buffer, which must outlive it.
rbe::dsrl::proxy<Trade> view = rbe::deserialize<Trade>(bytes, rbe::dsrl::lazy);

auto price = view.field<"price">();   // this field only, converted on access

Pick it to route or filter on a couple of fields out of a large message. See Read fields lazily for everything a view offers.

In place: the buffer is the object

For a trivially wirable type the wire bytes are the object's bytes: the buffer is reinterpreted, nothing is decoded and nothing is copied. The buffer must be aligned for the type.

// The buffer *is* the object: no copy, no conversion. Only for trivially wirable types, and the buffer
// must be aligned for T (alignas(Tick) below) or the behavior is undefined.
Tick const& tick = rbe::deserialize<Tick>(bytes, rbe::dsrl::in_place);

Over a mutable buffer, in_place_mut returns a mutable reference: writes go straight to the buffer.

// Same as in_place over a mutable buffer: writing through the reference writes the buffer
Tick& tick = rbe::deserialize<Tick>(bytes, rbe::dsrl::in_place_mut);
tick.quantity += 1;

For a trivially wirable type all three modes end in the same memcpy; the choice only matters for types that need a conversion.

See also: Deserialization reference.